SWAGOLX.EXE (c) 1993 GDSOFT ALL RIGHTS RESERVED 00022 FREQUENTLY ASKED QUESTIONS/TUTORIALS 1 05-28-9313:45ALL SWAG SUPPORT TEAM General PASCAL FAQ IMPORT 289 ANSWERS TO FREQUENTLY ASKED PASCAL QUESTIONSπ ============================================ππ1...π π Q. How do I pass an error level code when my program finishes? π π A. The halt procedure takes an optional parameter of type word. Thus - π π halt(1); π π terminates the program with an errorlevel of 1. If halt is used without π a parameter it is the same as - π π halt(0); π π Note: When a program is terminated using the halt procedure any exit π procedure that has previously been set up is executed. πππ2...π π Q. How do I empty the keyboard buffer? π π A. There are several ways that this can be achieved. However the safest π is - π π while Keypressed do ch := ReadKey; π π This requires that a variable ch of type char is declared and the crt π unit be used. To do it without using a variable - π π while Keypressed do while ReadKey = #0 do; π π or if using TP6 with extended syntax enabled - π π while KeyPressed do ReadKey; π π If you do not wish to incur the substantial overhead involved with the π use of the CRT unit and there is no requirement for the program to run π under a multi-tasker - π π var π head : byte absolute $40:$1c; π tail : byte absolute $40:$1e; π π tail := head; ππ3...ππ Q. When I redirect the screen output of my programs to a file the file is π empty and the output still appears on the screen. What am I doing π wrong? π π A. You are probably using the CRT unit and its default method of writing π to stdout is by direct screen writes. In order to enable output to be π redirected all writes must be done by DOS. Setting the variable π DirectVideo to false has no effect on redirection as all it does is use π the BIOS for screen writes - not DOS. π π To enable redirection you must not use the CRT unit π π OR π π assign(output,''); π rewrite(output); π π This will make all output go through DOS and thus can be redirected if π desired. To restore the default situation - π π AssignCRT(output); rewrite(output); πππ4...ππ Q. How do I make a string that is lower or mixed case all uppercase?π π A. There are several ways to convert lower case characters to upper case. π Here are some of them. π π As a procedure (excluding asm code this is the fastest way) π π procedure StrUpper(var st: string); π var x : byte; π begin π for x := 1 to length(st) do π st[x] := UpCase(st[x]); π end; π π As a function (slower but sometimes more convenient) - π π function StrUpper(st: string): string; π var x : byte; π begin π StrUpper[0] := st[0]; π for x := 1 to length(st) do π StrUpper[x] := UpCase(st[x]); π end; π π Both the above are suitable for the English language . However from π version 4.0 onwards, DOS has had the facility to do this in a way that π is country (language) specific. I am indebted to Norbert Igl for the π basic routine. I have modified his code slightly. For the anti-goto π purists this is a good example of a goto that is convenient, efficient, π self-documenting and structured. The dos calls would make this method π the slowest of all. π π function StrUpper(s: string): string; π { Country specific string-to-uppercase conversion. Requires DOS unit } π label π fail; π var π regs : registers; π x : byte; π begin π if lo(DosVersion) >= 4 then begin π with regs do begin π ax := $6521; π ds := seg(s); π dx := ofs(s[1]); π cx := length(s); π msdos(regs); π if odd(flags) then { the attempted conversion failed so } π goto fail; π end; { with } π end { if DOS >= 4.0 } else π fail: π for x := 1 to length(s) do π s[x] := UpCase(s[x]); π StrUpper := s; π end; { StrUpper } ππππ5...π π Q. When I include ANSI codes in a string and write that string to the π screen the actual codes appear on the screen, rather than the results π they are supposed to achieve. π π A. In order for ANSI codes to be interpreted, screen writes must be π directed through DOS and there must have been a suitable driver loaded π via the config.sys file at boot time. All output can be directed π through DOS and the driver by - π π Not using the crt unit π π OR - π π assign(output,''); π rewrite(output); π π in which case ALL screen writes are "ANSI code sensitive" π π OR - π π You can set up write procedures that will be "ANSI code sensitive". π (You will need an initialisation procedure to set this up.) π π var π ansi : text; π π procedure AssignANSI(var ansifile : text); π begin π assign(ansifile,'CON'); π rewrite(ansifile); π end; { AssignANSI } π π procedure WriteANSI(var st: string); π begin π write(ansi,st) π end; { WriteANSI } π π procedure WriteLnANSI(var st: string); π begin π writeANSI(st); π writeln(ansi); π end; { WriteANSI } π π ObviousLy, if the ANSI.SYS driver (or an equivalent) is not installed π none of the above can work. π π Setting the variable DirectVideo in the CRT unit to false will not π achieve the desired result as this merely turns off direct screen π writes and uses the BIOS for all screen output. πππ6...π π Q. When I try to shell to DOS nothing happens. What am I doing wrong? π π A. In order to be able to execute any child process there must be π sufficient memory available for it to load and execute. Unless you π advise differently at compile time, a Turbo Pascal program grabs all π available memory for itself when it is loaded. To reserve memory for a π child process use the compiler memory directive - π π {$M 16384,0,0) π the default is - π {$M 16384,0,655360} π π The first figure - StackMin - is the amount of memory to be allocated π for the stack: π π Minimum is: 1024 π Default is: 16384 π Maximum is: 65520 π π The next figure - HeapMin -is the minumum amount of memory to be π allocated for the heap. If there is less memory available than this π figure the program will not load. π π Minimum is: 0 π Default is: 0 π Maximum is: 655360 In practice it will be the amount of free π memory less the space required for the stack, π less the code space of the program. You should π set this to 0 unless your program uses the π heap. In that case, set it to the lowest π possible figure to prevent heap allocation π errors. In most cases it is best to leave it π at zero and do error checking within the π program for sufficient memory at allocation π time. π π The last figure is the crucial on as regards child processes. It π should always be low enough to leave memory left over for a child π process and high enough not to cause problems for the program when π allocating heap memory. π π Minimum is: HeapMin π Default is: 655360 π Maximum is: 655360 If less than the requested amount is available π no error is reorted. Instead all available π memory is allocated for heap use. ππππ7...π π Q. How do I shell to DOS? π π A. SwapVectors; π exec(GetEnv('COMSPEC',''); π SwapVectors; π π Read previous section on memory allocation. π π I find that it is a good idea to write my own Exec function which will π do everything that is needed for me. I have it return an integer value π that is the DosError code. π π function Exec(p1,p2: string); π begin π SwapVectors; π Dos.Exec(p1,p2); π SwapVectors; π Exec := DosError; π end; π π This enables me to have a statement such as - π π ReportError(Exec(GetEnv('COMPSEC'),'')); π π Now you can have an empty ReportError procedure or you can make it π report the error - whatever is suitable for you application. πππ8...π π Q. When I execute a child process redirection does not work. Why? π π A. Redirection of a child process's output only works if it is run under π another copy of the command processor. So - π π exec('YourProg.exe',' > nul'); will not work but π exec(GetEnv('COMSPEC'),'/c YourProg > nul'); will work. πππ9...ππ Q. How do I read an errorlevel from a child process?ππ A. After executing a child process the errorlevel returned can be readπ by calling the DosExitCode function which returns a word. The lowπ byte is the errorlevel. A full description is in the manual.ππ If the command interpreter is the child process and it in turnπ executes a child process then the errorlevel of the second childπ process cannot be read without resorting to some trickery.πππ10...ππ Q. When I read a text file that has lines exceeding 255 characters Iπ lose all those characters from the 256th one on each time there is aπ line that exceeds that length. How can I prevent this?ππ A. Turbo Pascal's readln procedure reads a line up to the 255thπ character then skips to the next line. To get around this youπ should declare a buffer at least as large as the longest possibleπ line and then use the read procedure. The best size for the bufferπ is a multiple of 2048 bytes.ππ constπ BufferSize = 2048;π LineLength = 78;π typeπ textbuffer = array[1..BufferSize] of char;π varπ st : string;π f : text;π buffer : textbuffer;ππ function ReadTxtLn(var tf: text; var s: string; max: byte): integer;π { Reads a string of a maximum length from a text file }π varπ len : byte absolute s;π beginπ len := 0;π {$I-}π while (len < max) and not eoln(tf) do beginπ inc(len);π read(tf);π end;π if eoln(tf) thenπ readln(tf);π ReadTxtLn := IOResult;π {$I+}π end; { ReadTxtLn }ππ beginπ assign(f,filename);π reset(f);π SetTextBuf(f,buffer);π while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) doπ writeln(st);π close(f);π end.πππ11...ππ Q. How do I convert nul terminated asciiz strings to Turbo Pascalπ strings?ππ A. Here is a function that will do that -ππ function Asc2Str(var s; max: byte): string;π { Converts an ASCIIZ string to a Turbo Pascal string }π { with a maximum length of max. }π var starray : array[1..255] of char absolute s;π len : integer;π beginπ len := pos(#0,starray)-1; { Get the length }π if (len > max) or (len < 0) then { length exceeds maximum }π len := max; { so set to maximum }π Asc2Str := starray;π Asc2Str[0] := chr(len); { Set length }π end; { Asc2Str }πππ12...ππ Q. How can I tell if a particular bit of a variable is set or not? How canπ I set it? How can I turn it off? How can I make a large bit map andπ then determine if a particular bit - say bit 10000 is on/of?ππ A. This question, or a variation of it, is one of the most commonly askedπ questions in the echo and there are several ways of doing what isπ wanted. None are necessarily right or wrong. The way I will describeπ is designed to take up as little code/data space as possible. I do notπ attempt to explain the theory behind these functions as this can beπ obtained from any good book. Question 16 also contains valuable extraπ help on the subject of truth tables.ππ The use of sets can be the best bit manipulation method if you haveπ control over the data being used. Here is an example of a byte variableπ for a BBS program which sets various user access level flags.ππ Bit 0 = Registered Userπ 1 = Twitπ 2 = Normalπ 3 = Extraπ 4 = Privilegedπ 5 = Visiting Sysopπ 6 = Assistant Sysopπ 7 = Sysopππ typeπ status_type = (Registered,π Twit,π Normal,π Extra,π Privileged,π VisitingSysop,π AssistantSysop,π Sysop);π status_level = set of status_type;ππ varπ access_flags : status_level;ππ Let us assume you have someone who logs on and you wish to determineπ his user access level. After reading access_flags from the user dataπ file -ππ if Sysop in access_flags then ....ππ To set the sysop flag -ππ access_flags := access_flags + [Sysop];ππ To reset the sysop flag -ππ access_flags := access_flags - [Sysop];ππ However on many occasions using a set may not be a suitable method.π You may simply need to know if bit 5 is set or not. Here is the methodπ that I consider the best -ππ function BitIsSet(var V, bit: byte): boolean;π beginπ BitIsSet := odd(V shr bit);π end;ππ To set a bit -ππ procedure SetBit(var V: byte; bit: byte);π beginπ V := V or (1 shl bit);π end;ππ To reset a bit -ππ procedure ResetBit(var V: byte; bit: byte);π beginπ V := V and not(1 shl bit);π end;ππ To toggle (flip) a bit -ππ procedure ToggleBit(var V: byte; bit: byte);π beginπ V := V xor (1 shl bit);π end;ππ Now a bit map can be made up from an array of bytes. If stored on theπ heap you can test any bit up to number 524159 (zero based). Here'sπ how.ππ typeπ map = array[0..maxsize] of byte;π { set maxsize to number of bits div 8 -1 needed in the bit map }ππ function BitSetInBitMap(var x; numb : longint): boolean;π { Tests the numb bit in the bitmap array }π var m: map absolute x;π beginπ BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7));π end;ππ procedure SetBitInBitMap(var x; numb: word);π { Sets the numb bit in the bitmap array }π var m: map absolute x;π beginπ m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7))π end;ππ procedure ResetBitInBitMap(var x; numb : longint);π { Resets the numb bit in the bitmap array }π var m: map absolute x;π beginπ m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7));π end;ππ procedure ToggleBitInBitMap(var x; numb : longint);π { Toggles (flips) the numb bit in the bitmap array }π var m: map absolute x;π beginπ m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7));π end;πππ13...ππ Q. How can I find a particular string in any file - text or binary?ππ A. The Boyer-Moore string search algorithm is considered to be the fastestπ method available. However in a rare worst-case scenario it can beπ slightly slower than a linear brute-force method. The followingπ demonstration program will show how it works and could easily beπ modified to allow for command line paramters etc.πππ program BMSearchDemo;ππ typeπ bigarray = array[0..32767] of byte;π baptr = ^bigarray;π BMTable = array[0..255] of byte;ππ constπ KeyStr : string = 'Put whatever you want found here';π fname : string = 'f:\Filename.txt';ππ varπ Btable : BMtable;π buffer : baptr;π f : file;π result,π position : word;π offset : longint;π finished,π Strfound : boolean;ππ procedure MakeBMTable(var t : BMtable; var s);π { Makes a Boyer-Moore search table. s = the search string t = the table }π varπ st : BMtable absolute s;π slen: byte absolute s;π x : byte;π beginπ FillChar(t,sizeof(t),slen);π for x := slen downto 1 doπ if (t[st[x]] = slen) thenπ t[st[x]] := slen - xπ end;ππ function BMSearch(var buff,st; size : word): word;π { Not quite a standard Boyer-Moore algorithm search routine }π { To use: pass buff as a dereferenced pointer to the buffer}π { st is the string being searched for }π { size is the size of the buffer }π { If st is not found, returns $ffff }π varπ buffer : bigarray absolute buff;π s : array[0..255] of byte absolute st;π len : byte absolute st;π s1 : string absolute st;π s2 : string;π count,π x : word;π found : boolean;π beginπ s2[0] := chr(len); { sets the length to that of the search string }π found := false;π count := pred(len);π while (not found) and (count < (size - len)) do beginπ if (buffer[count] = s[len]) then { there is a partial match } beginπ if buffer[count-pred(len)] = s[1] then { less partial! } beginπ move(buffer[count-pred(len)],s2[1],len);π found := s1 = s2; { if = it is a complete match }π BMSearch := count - pred(len); { will stick unless not found }π end;π inc(count); { bump by one char - match is irrelevant }π endπ elseπ inc(count,Btable[buffer[count]]); { no match so increment maximum }π end;π if not found thenπ BMSearch := $ffff;π end; { BMSearch }πππ beginπ new(buffer);π assign(f,fname);π reset(f,1);π offset := 0;π MakeBMTable(Btable,KeyStr);π repeatπ BlockRead(f,buffer^,sizeof(buffer^),result);π position := BMSearch(buffer^,KeyStr,result);π finished := (result < sizeof(buffer^)) or (position <> $ffff);π if position = $ffff thenπ inc(offset,result);π Strfound := position <> $ffff;π until finished;π close(f);π if Strfound thenπ writeln('Found at offset ',offset)π elseπ writeln('Not found');π end.ππ14...ππ Q. How can I put a apostrophe in a string?ππ A. Just put in extra apostrophes. If you want st to be equal to theπ string -π The word 'quoted' is in quotesπ do this -π st := 'The word ''quoted'' is in quotes';ππ if you want the following to be written to screen -π 'This is a quoted string'π do this -π writeln('''This is a quoted string''');πππ15...ππ Q. What are the best books to purchase to help me learn Turbo Pascal?ππ A. There are many good books for learners. Here are a few -ππ Complete Turbo Pascal - Third Edition - Jeff Duntemannπ Mastering Turbo Pascal 6 - Tom Swannπ Turbo Pascal - The Complete Reference - O'Brien.ππ For advanced users there are also many good books. Here are a fewπ that I have found useful - (Those marked with an asterisk are notπ purely for Turbo Pascal)ππ Turbo Pascal 6 - Techniques and Utilities - Rubenkingπ Turbo Pascal Internals - Tischerπ * PC System Programming for Developers - Tischerπ * Undocumented DOS - Schulmanππ Any learner would be well advised to obtain a well known libraryπ such as Technojock's Turbo Toolkit (TTT) which is shareware andπ study the source code.ππ 16.ππ Q. hat are "truth tables" and how do they work?ππ A. Truth tables are a set of rules that are used to determine the result ofπ logical operations. The logical operators are -ππ NOTπ ANDπ ORπ XOR.ππ Here is a brief explanation of truth tables. When two values areπ logically compared by using a logical operator each bit of one value isπ directly compared to the corresponding bit in the other value and theπ same bit in the returned value is set or reset according to theπ following truth table.ππ NOT AND OR XORπ not 1 = 0 0 and 0 = 0 0 or 0 = 0 0 xor 0 = 0π not 0 = 1 0 and 1 = 0 0 or 1 = 1 0 xor 1 = 1π 1 and 0 = 0 1 or 0 = 1 1 xor 0 = 1π 1 and 1 = 1 1 or 1 = 1 1 xor 1 = 0ππ NOT reverses the bit.π AND sets the returned bit if both compared bits are set.π OR sets the returned bit if either of the compared bits are set.π XOR sets the returned bit if the compared bits are not the same.πππ 17.ππ Q. What are pointers and how can I use them? I have heard that they areπ variables that can be created and discarded as required thus savingπ memory. Is this true?ππ A. A pointer is a variable that contains a memory address.ππ The heap is all of that memory allocated by DOS to a program for itsπ use that has not been used by the program for its code, global data orπ stack.ππ Dynamic variables are variables that have had space allocated for themπ on the heap.ππ Dynamic variables have no identifier (are unnamed). Because of thisπ they need an associated variable that can be used to find where theyπ reside in memory. Pointers are ideal for this but need some method toπ define what type of data it is that they are pointing at. Pascalπ provides this method.ππ typeπ Str10Ptr = ^string[10];π { This means Str10Ptr is a pointer that points to data of type }π { string[10]. }π varπ S : Str10Ptr;ππ In the above example S is a pointer that has been defined as pointingπ to an address in memory that will contain (or should contain) data ofπ type string[10].ππ However how does S get this value? How does it know where that data'sπ address is supposed to be? Well until the programmer allocates memoryπ for that data S's value is undefined, so it could be literallyπ pointing anywhere. So it is *vital* that before we try to use it toπ use/assign data from/to that memory location we give S a memoryπ address that is not being used for any other purpose at the moment andπ that is big enough to hold the data that we want to place into it - inπ this case at least 11 bytes. We do this by -ππ new(S);ππ Pascal has now allocated at least 11 bytes of heap and has allocated Sπ with the address of the FIRST byte of that allocation.ππ Ok... so far so good! How do we access that data (remembering that itπ has no name). Well we "dereference" the pointer. This is done byπ placing a carat sign immediately following the pointer's identifier.ππ S^ := 'Joe Bloggs';ππ This statement actually means "Place the string 'Joe Bloggs' into theπ memory address that S contains". This is referred to as "derferencing"π the pointer S.ππ To "reference" a dynamic variable we "dereference" its associatedπ pointer variable. We cannot say -ππ S := 'Joe Bloggs';ππ because S is a pointer and that would be trying to give a pointer aπ string type value - a compiler "type mismatch" would occur. So everyπ time we wish to access that dynamic variable we dereference it.ππ To delete the dynamic variable once it is of no further use is just aπ matter of -ππ dispose(S);ππ What this statement does is release the memory previously used by S^π and make it available to be used for other purposes by the program.π Depending on the version of Pascal you are using it may not erase orπ alter the contents of that memory and it may not give S a new value.π However any attempt to dereference S is an error as the integrity ofπ that memory location has been lost - it may have been allocated toπ other data.ππ Pointers do not *have* to point to a memory location in the heap orπ even have their value always allocated by using the New procedure. Anyπ valid memory address can be assigned to them and then they can beπ dereferenced as shown above. As a simple example of this lets say youπ want to examine the contents of the 16 byte area at $40:$f0 (the ICAπ area). You could - (TP specific)ππ typeπ ICA_Ptr = ^array[0..15] of byte;π varπ B : byte;π ICA : ICA_Ptr;ππ ICA := ptr($40,$f0);ππ Now ICA points to the address specified and you can dereference it -ππ B := ICA^[10];ππ Hope that helps get you started into the complex world of memoryπ management and manipulation using pointers. There are countlessπ permutations and methods that can be used.πππ 18.ππ Q. How do I do word wrap?ππ A. The demo program WRAP.PAS in this archive demonstrates both word wrapπ and the justifying of text.πππππππππππ 2 05-28-9313:45ALL SWAG SUPPORT TEAM Dos Programming FAQ IMPORT 491 =====================πsection 1. General questionsπ 101. Why won't my code work?π 102. What is this newsgroup about?π 103. What's the difference from comp.sys.ibm.pc.programmer?π 104. What other newsgroups should I know about?ππsection 2. Compile and linkπ 201. What the heck is "DGROUP > 64K"?π 202. How do I fix "automatic data segment exceeds 64K" or "stackπ plus data exceed 64K"?π 203. Will Borland C code and Microsoft C code link together?π 204. Why did my program bomb at run time with "floating pointπ formats not linked"?π 205. Why did my program bomb with "floating point not loaded"?π 206. How can I change the stack size in Borland's C compilers?π 207. What's the format of an .OBJ file?π 208. What's the format of an .EXE header?π 209. What's the difference between .COM and .EXE formats?ππsection 3. Keyboardπ 301. How can I read a character without echoing it to the screen,π and without waiting for the user to press the Enter key?π 302. How can I find out whether a character has been typed, withoutπ waiting for one?π 303. How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?π 304. How can I disable the print screen function?π 305. How can my program turn NumLock (CapsLock, ScrollLock) on/off?π 306. How can I speed up the keyboard's auto-repeat?π 307. What is the SysRq key for?π 308. How can my program tell what kind of keyboard is on the system?π 309. How can I tell if input, output, or stderr has been redirected?ππsection 4. Disks and filesπ 401. What drive was the PC booted from?π 402. How can I boot from drive b:?π 403. Which real and virtual disk drives are valid?π 404. How can I make my single floppy drive both a: and b:?π 405. Why won't my C program open a file with a path?π 406. How can I redirect printer output to a file?π 407. How can my program open more files than DOS's limit of 20?π 408. How can I read, create, change, or delete the volume label?π 409. How can I get the disk serial number?π 410. What's the format of .OBJ, .EXE., .COM files?ππsection 5. Serial ports (COM ports)π 501. How do I set my machine up to use COM3 and COM4?π 502. How do I find the I/O address of a COM port?π 503. But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8,π and 2E8?π 504. How do I configure a COM port and use it to transmit data?πsection 6. Other hardware questions and problemsπ 601. Which 80x86 CPU is running my program?π 602. How can a C program send control codes to my printer?π 603. How can I redirect printer output to a file?π 604. Which video adapter is installed?π 605. How do I switch to 43- or 50-line mode?π 606. How can I find the Microsoft mouse position and button status?π 607. How can I access a specific address in the PC's memory?π 608. How can I read or write my PC's CMOS memory?π 609. How can I access memory beyond 640K?πsection 7. Other software questions and problemsπ 701. How can a program reboot my PC?π 702. How can I time events with finer resolution than the systemπ clock's 55 ms (about 18 ticks a second)?π 703. How can I find the error level of the previous program?π 704. How can a program set DOS environment variables?π 705. How can I change the switch character to - from /?π 706. Why does my interrupt function behave strangely?π 707. How can I write a TSR (terminate-stay-resident) utility?π 708. How can I write a device driver?π 709. What can I use to manage versions of software?π 710. What's this "null pointer assignment" after my C programπ executes?ππsection A. Downloadingπ A01. What is garbo? What is wustl?π A02. What are Simtel and "mirror sites"? What good are they?π A03. Where do I find program <mumble>?π A04. How can I check Simtel or garbo before I post a request for aπ program?π A05. How do I download and decode a program I found?π A06. Where is UUDECODE?π A07. Why do I get errors when extracting from a ZIP file Iπ downloaded?ππsection B. Vendors and productsπ B01. How can I contact Borland?π B02. How can I contact Microsoft?π B03. What's the current version of PKZIP?π B04. What's in Borland Pascal/Turbo Pascal 7.0?π B05. What's in Microsoft C/C++ 7.0?πsection C. More informationπ C01. Are there any good on-line references for PC hardwareπ components?π C02. Are there any good on-line references for PC interrupts?π C03. What and where is "Ralf Brown's interrupt list"?π C04. Where can I find lex, yacc, and language grammars?π C05. What's the best book to learn programming?π C06. Where are FAQ lists archived?π C07. Where can I get the latest copy of this FAQ list?ππππsection 1. General questionsπ============================ππQ101. Why won't my code work?ππ First you need to try to determine whether the problem is in yourπ use of the programming language or in your use of MSDOS and your PCπ hardware. (Your manual should tell you which features are standardπ and which are vendor- or MSDOS- or PC-specific. You _have_ readπ your manual carefully, haven't you?)ππ If the feature that seems to be working wrong is something relatedπ to your PC hardware or to the internals of MS-DOS, this group is theπ right place to ask. (Please check this list first, to make sureπ your question isn't already answered.)ππ On the other hand, if your problem is with the programming language,π the comp.lang hierarchy (including comp.lang.pascal and comp.lang c)π is probably a better resource. Please read the other group's FAQπ list thoroughly before posting. (These exist in comp.lang.c,π comp.lang.c++, comp.lang.modula3, comp.lang.lisp, comp.lang.perl;π they may exist in other groups as well.) It's almost never a goodπ idea to crosspost between this group and a language group.ππ Before posting in either place, try to make your program as small asπ possible while still exhibiting the bad behavior. Sometimes thisπ alone is enough to show you where the trouble is. Also edit yourπ description of the problem to be as short as possible. This makesπ it look more like you tried to solve the problem on your own, andπ makes people more inclined to try to help you.ππ When you do post a question, it's good manners to say "email please;π I'll post a summary." Then everybody else in the group doesn't haveπ to read ten virtually identical responses. Of course, then you haveπ to follow through. A summary is not simply pasting together all theπ email you received. Instead, write your own (brief) description ofπ the solution: this is the best way to make sure you reallyπ understand it. Definitely don't repost people's cute signatures.ππQ102. What is this newsgroup about?ππ comp.os.msdos.programmer (comp.sys.ibm.pc.programmer until Septemberπ 1990) concerns programming for MS-DOS systems. The article "USENETπ Readership report for Nov 92" in news.lists shows 42,000 readers ofπ this newsgroup worldwide. Traffic was 1090.7 Kbytes (exclusive ofπ crossposts), comprised in 611 articles.ππ Much of our traffic is about language products (chiefly from Borlandπ and Microsoft). More programming topics focus on C than on any oneπ other language.ππ Since most MS-DOS systems run on hardware that is roughly compatibleπ with the IBM PC, on Intel 8088, 80188, or 80x86 chips, we tend toπ get a lot of questions and answers about programming other parts ofπ the hardware.ππQ103. What's the difference from comp.sys.ibm.pc.programmer?ππ c.s.i.p.programmer is the old name of comp.os.msdos.programmer, andπ has been obsolete since September 1990. However, many systems haveπ not removed the old group, or have removed it but aliased it to theπ new name. This means that some people still think they're postingπ to c.s.i.p.programmer even though they're actually posting toπ c.o.m.programmer.ππ You can easily verify the non-existence of c.s.i.p.programmer byπ reference to the "List of Active Newsgroups" posted to news.groups.π It's available as /pub/usenet/news.answers/active-newsgroups/part1π from the archives (see "Where are FAQ lists archived?" in section C,π "More information").ππQ104. What other newsgroups should I know about?ππ Your best bet is to read the periodic information postings in theπ comp.binaries.ibm.pc newsgroup. Specially helpful articles:π Using the comp.binaries.ibm.pc.d groupsπ Beginner's guide to binariesπ Starter kitπ About archives and archiversπ Please wait for these articles to come around; don't post a request.ππ Also check out news.announce.newusers, even if you're not a newπ user. You may be surprised how much useful information is in theπ monthly postings there. Lots of old-timers also get useful stuffπ from news.newusers.questions, especially the periodic postings.ππ Remember that it's good manners to subscribe to any newsgroup andπ read it for a while before you post a question. When you post, it'sπ also good manners to ask for replies to be emailed and then to postπ a summary, which you've edited down to the absolute minimum size.ππ You may also be interested in the following newsgroups. Caution:π Some of them have specialized charters; you'll probably get (andπ deserve) some flames if you post to an inappropriate group.ππ - misc.forsale.computers and misc.forsale.computers.pc-clone areπ where you post notices of equipment, software, or computer booksπ that you want to sell. Please don't post or crosspost thoseπ notices to comp.os.msdos.programmer.ππ - comp.os.ms-windows.programmer.tools and ...misc (formerly part ofπ comp.windows.ms.programmer): Similar to this group, but focusπ on programming for the MS-Windows platform.ππ - comp.sys.ibm.pc.hardware is for more hardware-oriented discussionsπ of the machines that run DOS.ππ - comp.binaries.ibm.pc.wanted: AFTER you have looked in the otherπ groups, this is the place to post a request for a particularπ binary program.ππ - comp.binaries.msdos.announce (moderated) explains how to use theπ archive sites, especially garbo and Simtel, and lists filesπ uploaded to them. Discussions belong in comp.binaries.msdos.d,π which replaced comp.binaries.ibm.pc.archives.ππ - comp.binaries.ibm.pc.d is for discussions about programs posted inπ comp.binaries.ibm.pc, and only those programs. This is a goodπ place to report bugs in the programs, but not to ask where to findπ them (see cbip.wanted, above). cbip.d is NOT supposed to be aπ general PC discussion group.ππ - comp.sources.misc: a moderated group for source code for manyπ computer systems. It tends to get lots of Unix stuff, but you mayπ also pick up some DOS-compatible code here.ππ - alt.sources: an unmoderated group for source code. Guidelines areπ posted periodically.ππ - Turbo Vision is a mailing list, not a newsgroup; send email toπ listserv@vtvm1.cc.vt.edu if you want to subscribe.πππsection 2. Compile and linkπ===========================ππQ201. What the heck is "DGROUP > 64K"?ππ DGROUP is a link-time group of data segments, and the compilerπ typically generates code that expects DS to be pointing to DGROUP.π (Exception: Borland's huge model has no DGROUP.)ππ Here's what goes into DGROUP:ππ - tiny model (all pointers near): DGROUP holds the entire program.ππ - small and medium models (data pointers near): DGROUP holds allπ globals and static variables including string literals, plus theπ stack and the heap.ππ - large, compact, and huge models in Microsoft (data pointers far):π DGROUP holds only initialized globals and static variablesπ including string literals, plus the stack and the near heap.ππ - large and compact models in Borland (data pointers far): DGROUPπ holds initialized and uninitialized globals and static variablesπ including string literals, but not the stack or heap.ππ - huge model in Borland (data pointers far): there is no DGROUP, soπ the 64K limit doesn't apply.ππ In all of the above, which is to say all six models in Microsoft Cπ and all but huge in Borland C, DGROUP is limited to 64K includingπ string literals (which are treated as static data). This limitationπ is due to the Intel CPU's segmented architecture.ππ See the next Q for possible remedies.ππ For more information, see topics like "memory models" and "memoryπ management" in the index of your compiler manual. Also seeπ TI738.ASC in PD1:<MSDOS.TURBO-C>BCHELP10.ZIP at Simtel for anπ extended general discussion of memory usage in Borland C programs,π of which much applies to any C compiler in DOS.ππQ202. How do I fix "automatic data segment exceeds 64K" or "stack plusπ data exceed 64K"?ππ These messages are a variation of "DGROUP > 64K". For causes,π please see the preceding Q.ππ If you get this error in tiny model, your program is simply too bigπ and you must use a different memory model. If you get this linkπ error in models S, C, M, L, or Microsoft's H, there are some thingsπ you can do. (This error can't occur in Borland's huge model.)ππ If you have one or two big global arrays, simply declare them far.π The compiler takes this to mean that any references to them will useπ 32-bit pointers, so they'll be in separate segments and no longerπ part of DGROUP.ππ Or you can use the /Gt[number] option with Microsoft or -Ff[=size]π with Borland C++ 2.0 and up. This will automatically put variablesπ above a certain size into their own segments outside of DGROUP.ππ Yet another option is to change global arrays to far pointers. Thenπ at the beginning of your program, allocate them from the far heapπ (_fmalloc in Microsoft, farmalloc in Borland).ππ Finally, you can change to huge model (with Borland compilers, notπ Microsoft). Borland's H model still uses far pointers by default,π but "sets aside the [64K] limit" and has no DGROUP group, accordingπ to the BC++ 2.0 Programmer's Guide. Microsoft's H model does useπ huge data pointers by default but retains DGROUP and its 64K limit,π so switching to the H model doesn't buy you anything if you haveπ DGROUP problems.ππQ203. Will Borland C code and Microsoft C code link together?ππ Typically this question is asked by someone who owns compiler A andπ is trying to write code to link with a third-party library that wasπ compiled under compiler B.ππ The answer to the question is, Not in general. Here are some of theπ reasons:ππ - "Helper" functions (undocumented functions for stack checking,π floating-point arithmetic, and operations on longs) differ betweenπ the two compilers.ππ - The compilers may embed instructions in the object code that tellπ the linker to look for their own run-time libraries.ππ Those problems will generate link-time errors. Others may not showπ up until run time:ππ - Borland's compact, large, and huge models don't assume DS=SS, butπ Microsoft's do. The -Fs option on the Borland compiler, or one ofπ the /A options on Microsoft, should take care of this problem --π once you know that's what's going on.ππ - Check conventions for ordering and packing structure members, andπ for alignment of various types on byte, word, paragraph, or otherπ boundaries. Again, you can generally adjust your code to match ifπ you know what conventions were used in compiling the "foreign"π libraries.ππ - Check the obvious and make sure that your code was compiled underπ the same memory model as the code you're trying to link with.π (That's necessary, but no guarantee. Microsoft and Borland don'tπ use exactly the same conventions for segments and groups,π particularly in the larger memory models.)ππ That said, there are some circumstances where you can link hybrids.π Your best chance of success comes if you avoid longs and floatingπ point, use only 16-bit pointers, suppress stack checking, andπ specify all libraries used in the link.ππQ204. Why did my program bomb at run time with "floating point formatsπ not linked"?ππ First, is that the actual message, or did it say "floating point notπ loaded"? If it was the latter, see the next Q.ππ You're probably using a Borland compiler for C or C++ (includingπ Turbo C and Turbo C++). Borland's compilers try to be smart and notπ link in the floating-point (f-p) library unless you need it. Alas,π they all get the decision wrong. One common case is where you don'tπ call any f-p functions, but you have %f or other f-p formats inπ scanf/printf calls. The cure is to call an f-p function, or atπ least force one to be present in the link.ππ To do that, define this function somewhere in a source file butπ don't call it:ππ static void forcefloat(float *p)π { float f = *p; forcefloat(&f); }ππ It doesn't have to be in the module with the main program, as longπ as it's in a module that will be included in the link.ππ A new solution for Borland C++ 3.0 was posted, but I don't own theπ product and have not been able to verify it. Insert theseπ statements in your program:ππ extern unsigned _floatconvert;π #pragma extref _floatconvertππQ205. Why did my program bomb with "floating point not loaded"?ππ That is Microsoft C's run-time message when the code requires aπ numeric coprocessor but your computer doesn't have one installed.ππ If the program is yours, relink it using the xLIBCE or xLIBCAπ library (where x is the memory model).ππQ206. How can I change the stack size in Borland's C compilers?ππ In Turbo C, Turbo C++, and Borland C++, you may not find "stackπ size" in the index but the global variable _stklen should be there.π The manual will instruct you to put a statement likeππ extern unsigned _stklen = 54321U;ππ in your code, outside of any function. You must assign the valueπ right in the extern statement; it won't work to assign a value atπ run time. (The "extern" in this context isn't ANSI C and ought notπ to be required, but the above statement is a direct quote from theπ Library Reference manual of Borland C++ 2.0.) The linker may giveπ you a duplicate symbol warning, which you can ignore.ππQ207. What's the format of an .OBJ file?ππ Here's what I've been told, though I have verified any of theseπ references myself:ππ - base .OBJ format: Intel's document number #121748-001, {8086π Relocatable Object Module Formats}. (Note however that bothπ Microsoft and Borland formats have extended the .OBJ format.)ππ - Microsoft-specific .OBJ formats: a "Microsoft OMF Specification"π (document number ??), as well as a section in the MS-DOSπ encyclopedia.ππ - A "tutorial on the .OBJ format" comes with the VAL experimentalπ linker, which is VAL-LINK.ARC in PD1:<MSDOS.PGMUTL> at Simtel.ππ If you have specific references, either to fpt-able documents or toπ published works (author, title, order number or ISBN), please emailπ them to brown@ncoast.org for inclusion in the next edition of thisπ list.ππQ208. What's the format of an .EXE header?ππ See pages 349-350 of {PC Magazine}'s June 30, 1992 issue (xi:12) forπ the old and new formats. For a more detailed layout, look under INTπ 21 function 4B in Ralf Brown's interrupt list. Ralf Brown's listπ includes extensions for Borland's TLINK and Borland debugger info.ππ Among the books that detail formats of executable files are {DOSπ Programmer's Reference: 2d Edition} by Terry Dettman and Jim Kyle,π ISBN 0-88022-458-4; and {Microsoft MS-DOS Programmer's Reference},π ISBN 1-55615-329-5.ππQ209. What's the difference between .COM and .EXE formats?ππ To oversimplify: a .COM file is a direct image of core, and an .EXEπ file will undergo some further relocation when it is run (and so itπ begins with a relocation header). A .COM file is limited to 64K forπ all segments combined, but an .EXE file can have as many segments asπ your linker will handle and be as large as RAM can take.ππ The actual file extension doesn't matter. DOS knows that a fileπ being loaded is in .EXE format if its first two bytes are MZ or ZM;π otherwise it is assumed to be in .COM format. For instance, I amπ told that DR-DOS 6.0's COMMAND.COM is in .EXE format.πππsection 3. Keyboardπ===================ππQ301. How can I read a character without echoing it to the screen, andπ without waiting for the user to press the Enter key?ππ The C compilers from Microsoft and Borland offer getch (or getche toπ echo the character); Turbo Pascal has ReadKey.ππ In other programming languages, load 8 in register AH and executeπ INT 21; AL is returned with the character from standard inputπ (possibly redirected). If you don't want to allow redirection, orπ you want to capture Ctrl-C and other special keys, use INT 16 withπ AH=10; this will return the scan code in AH and ASCII code (ifπ possible) in AL, except that AL=E0 with AH nonzero indicates one ofπ the grey "extended" keys was pressed. (If your BIOS doesn'tπ support the extended keyboard, use INT 16 function 0 not 10.)ππQ302. How can I find out whether a character has been typed, withoutπ waiting for one?ππ In Turbo Pascal, use KeyPressed. Both Microsoft C and Turbo C offerπ the kbhit( ) function. All of these tell you whether a key has beenπ pressed. If no key has been pressed, they return that informationπ to your program. If a keystroke is waiting, they tell your programπ that but leave the key in the input buffer.ππ You can use the BIOS call, INT 16 function 01 or 11, to checkπ whether an actual keystroke is waiting; or the DOS call, INT 21π function 0B, to check for a keystroke from stdin (subject toπ redirection). See Ralf Brown's interrupt list.ππQ303. How can I disable Ctrl-C/Ctrl-Break and/or Ctrl-Alt-Del?ππ You can download the file PD1:<MSDOS.KEYBOARD>CADEL.ZIP from Simtel.π It contains a TSR to disable those keys, with source code in ASM.ππ To disable only Ctrl-Alt-Del (actually, to change the boot keys toπ leftShift-Alt-Del), use DEBOOT.COM. Along with KEYKILL.COM, whichπ lets you disable up to three keys of your choice, it is at Simtel inπ the file PD1:<MSDOS.KEYBOARD>KEYKILL.ARC.ππ C programmers who simply want to make sure that the user can'tπ Ctrl-Break out of their program can use the ANSI-standard signal( )π function; the Borland compilers also offer ctrlbrk( ) for handlingπ Ctrl-Break. However, if your program uses normal DOS input, theπ characters ^C will appear on the screen when the user presses Ctrl-Cπ or Ctrl-Break. There are many ways to work around that, including:π use INT 21 function 7, which allows redirection but doesn't displayπ the ^C (or echo any other character, for that matter); or use INT 16π function 0 or 10; or call _bios_keybrd( ) in MSC or bioskey( ) inπ BC++; or hook INT 9 to discard Ctrl-C and Ctrl-Break before theπ regular BIOS keyboard handler sees them; etc., etc.ππ You should be aware that Ctrl-C and Ctrl-Break are processed quiteπ differently internally. Ctrl-Break, like all keystrokes, isπ processed by the BIOS code at INT 9 as soon as the user presses theπ keys, even if earlier keys are still in the keyboard buffer: byπ default the handler at INT 1B is called. Ctrl-C is not special toπ the BIOS, nor is it special to DOS functions 6 and 7; it _is_π special to DOS functions 1 and 8 when at the head of the keyboardπ buffer. You will need to make sure BREAK is OFF to prevent DOSπ polling the keyboard for Ctrl-C during non-keyboard operations.ππ Some good general references are {Advanced MS-DOS} by Ray Duncan,π ISBN 1-55615-157-8; {8088 Assembler Language Programming: The IBMπ PC}, ISBN 0-672-22024-5, by Willen & Krantz; and {COMPUTE!'s Mappingπ the IBM PC}, ISBN 0-942386-92-2.ππQ304. How can I disable the print screen function?ππ There are really two print screen functions: 1) print currentπ screen snapshot, triggered by PrintScreen or Shift-PrtSc orπ Shift-grey*, and 2) turn on continuous screen echo, started andπ stopped by Ctrl-P or Ctrl-PrtSc.ππ 1) Screen snapshot to printerπ --------------------------ππ The BIOS uses INT 5 for this. Fortunately, you don't need to messπ with that interrupt handler. The standard handler, in BIOSes datedπ December 1982 or later, uses a byte at 0040:0100 (alias 0000:0500)π to determine whether a print screen is currently in progress. If itπ is, pressing PrintScreen again is ignored. So to disable the screenπ snapshot, all you have to do is write a 1 to that byte. When theπ user presses PrintScreen, the BIOS will think that a print screen isπ already in progress and will ignore the user's keypress. You canπ re-enable PrintScreen by zeroing the same byte.ππ Here's some simple code:ππ void prtsc_allow(int allow) /* 0=disable, nonzero=enable */ {π unsigned char far* flag = (unsigned char far*)0x00400100UL;π *flag = (unsigned char)!allow;π }ππ 2) Continuous echo of screen to printerπ ------------------------------------ππ If ANSI.SYS is loaded, you can easily disable the continuous echo ofπ screen to printer (Ctrl-P or Ctrl-PrtSc). Just redefine the keys byπ "printing" strings like these to the screen (BASIC print, C printf,π Pascal Write statements, or ECHO command in batch files):ππ <27>[0;114;"Ctrl-PrtSc disabled"pπ <27>[16;"^P"pππ Change <27> in the above to an Escape character, ASCII 27.ππ If you haven't installed ANSI.SYS, I can't offer an easy way toπ disable the echo-screen-to-printer function. Please send any testedπ solutions to brown@ncoast.org and I'll add them to this list.ππ Actually, you might not need to disable Ctrl-P and Ctrl-PrtSc. Ifπ your only concern is not locking up your machine, when you see theπ "Abort, Retry, Ignore, Fail" prompt just press Ctrl-P again and thenπ I. As an alternative, install one of the many print spoolers thatπ intercept printer-status queries and always return "Printer ready".ππQ305. How can my program turn NumLock (CapsLock, ScrollLock) on or off?ππ You need to twiddle bit 5, 6, or 4 of location 0040:0017. Here'sπ some code: lck( ) turns on a lock state, and unlck( ) turns it off.π (The status lights on some keyboards may not reflect the change. Ifπ yours is one, call INT 16 function 2, "get shift status", and thatπ may update them. It will certainly do no harm.)ππ #define NUM_LOCK (1 << 5)π #define CAPS_LOCK (1 << 6)π #define SCRL_LOCK (1 << 4)π void lck(int shiftype) {π char far* kbdstatus = (char far*)0x00400017UL;π *kbdstatus |= (char)shiftype;π }π void unlck(int shiftype) {π char far* kbdstatus = (char far*)0x00400017UL;π *kbdstatus &= ~(char)shiftype;π }ππQ306. How can I speed up the keyboard's auto-repeat?ππ The keyboard speed has two components: delay (before a key that youπ hold down starts repeating) and typematic rate (the speed once theπ key starts repeating). Most BIOSes since 1986 let software changeπ the delay and typematic rate by calling INT 16 function 3, "setπ typematic rate and delay"; see Ralf Brown's interrupt list. If youπ have DOS 4.0 or later, you can use the MODE CON command that you'llπ find in your DOS manual.ππ On 83-key keyboards (mostly XTs), the delay and typematic rate can'tπ easily be changed. According to the {PC Magazine} of 15 Jan 1991,π page 409, to adjust the typematic rate you need "a memory-residentπ program which simply '[watches]' the keyboard to see if you'reπ holding down a key ... and after a certain time [starts] stuffingπ extra copies of the held-down key into the buffer." No source codeπ is given in that issue; but I'm told that the QUICKEYS utility thatπ {PC} published in 1986 does this sort of watching; you can downloadπ source and object code in PD1:<MSDOS.PCMAG>VOL5N05.ARC from Simtel.ππQ307. What is the SysRq key for?ππ There is no standard use for the key. The BIOS keyboard routines inπ INT 16 simply ignore it; therefore so do the DOS input routines inπ INT 21 as well as the keyboard routines in libraries supplied withπ high-level languages.ππ When you press or release a key, the keyboard triggers hardware lineπ IRQ1, and the CPU calls INT 9. INT 9 reads the scan code from theπ keyboard and the shift states from the BIOS data area.ππ What happens next depends on whether your PC's BIOS supports anπ enhanced keyboard (101 or 102 keys). If so, INT 9 calls INT 15π function 4F to translate the scan code. If the translated scan codeπ is 54 hex (for the SysRq key) then INT 9 calls INT 15 function 85π and doesn't put the keystroke into the keyboard buffer. The defaultπ handler of that function does nothing and simply returns. (If yourπ PC has an older BIOS that doesn't support the extended keyboards,π INT 15 function 4F is not called. Early ATs have 84-key keyboards,π so their BIOS calls INT 15 function 85 but nor 4F.)ππ Thus your program is free to use SysRq for its own purposes, but atπ the cost of some programming. You could hook INT 9, but it'sπ probably easier to hook INT 15 function 85, which is called whenπ SysRq is pressed or released.ππQ308. How can my program tell what kind of keyboard is on the system?ππ Ralf Brown's Interrupt List includes MEMORY.LST, a detailedπ breakdown by Robin Walker of the contents of the BIOS system blockπ that starts at 0040:0000. Bit 4 of byte 0040:0096 is "1=enhancedπ keyboard installed". C code to test the keyboard type:π char far *kbd_stat_byte3 = (char far *)0x00400096UL;π if (0x10 & *kbd_stat_byte3)π /* 101- or 102-key keyboard is installed */ππ {PC Magazine}'s 15 Jan 1991 issue suggests on page 412 that "forπ some clones [the above test] is not foolproof". If you use thisπ method in your program you should provide the user some way toπ override this test, or at least some way to tell your program toπ assume a non-enhanced keyboard. The {PC Magazine} article suggestsπ a different approach to determining the type of keyboard.ππQ309. How can I tell if input, output, or stderr has been redirected?ππ Normally, input and output are associated with the console (i.e.,π with the keyboard and the screen, respectively). If either is not,π you know that it has been redirected. Some source code to checkπ this is available at the usual archive sites.ππ If you program in Turbo Pascal, download the /pc/ts/tspa*.zipπ collection of Turbo Pascal units from garbo; or from Simtel,π PD1:<MSDOS.TURBOPAS>TSPA*.ZIP. (Choose TSPA3060.ZIP, TSPA3055.ZIP,π TSPA3050.ZIP, or TSPA3040.ZIP for Turbo Pascal 6.0, 5.5, 5.0, or 4.0π respectively.) Source code is not included. Also see theπ information in garbo.uwasa.fi:/pc/ts/tsfaq*.zip Frequently Askedπ Questions, the Turbo Pascal section.ππ If you program in C, use isatty( ) if your implementation has it.π Otherwise, you can download PD1:<MSDOS.SYSUTL>IS_CON10.ZIP fromπ Simtel; it includes source code.ππ Good references for the principles are {PC Magazine} 16 Apr 1991π (vol 10 nr 7) pg 374; Ray Duncan's {Advanced MS-DOS}, ISBNπ 1-55615-157-8, or Ralf Brown's interrupt list for INT 21 functionπ 4400; and Terry Dettman and Jim Kyle's {DOS Programmer's Reference:π 2d edition}, ISBN 0-88022-458-4, pp 602-603.πππIf the posting date is more than six weeks in the past, see instructionsπin part 4 of this list for how to get an updated copy.ππ Copyright (C) 1992 Stan Brown, Oak Road Systemsπππsection 4. Disks and filesπ===========================ππQ401. What drive was the PC booted from?ππ Under DOS 4.0 or later, load 3305 hex into AX; do an INT 21. DL isπ returned with an integer indicating the boot drive (1=A:, etc.).ππQ402. How can I boot from drive b:?ππ Download PD1:<MSDOS.DSKUTL>BOOT_B.ZIP (shareware) from Simtel. Theπ included documentation says it works by writing a new boot sector onπ a disk in your a: drive that redirects the boot to your b: drive.ππQ403. Which real and virtual disk drives are valid?ππ Use INT 21 function 29 (parse filename). Point DS:SI at a null-π terminated ASCII string that contains the drive letter and a colon,π point ES:DI at a 37-byte dummy FCB buffer, set AX to 2900h, and doπ an INT 21. On return, AL is FF if the drive is invalid, somethingπ else if the drive is valid. RAM disks and SUBSTed drives areπ considered valid.ππ Unfortunately, the b: drive is considered valid even on a single-π diskette system. You can check that special case by interrogatingπ the BIOS equipment byte at 0040:0010. Bits 7-6 contain the one lessπ than the number of diskette drives, so if those bits are zero youπ know that b: is an invalid drive even though function 29 says it'sπ valid.ππ Following is some code originally posted by Doug Dougherty, with myπ fix for the b: special case, tested only in Borland C++ 2.0 (inπ the small model):ππ #include <dos.h>π void drvlist(void) {π char *s = "A:", fcb_buff[37];π int valid;π for ( ; *s<='Z'; (*s)++) {π _SI = (unsigned) s;π _DI = (unsigned) fcb_buff;π _ES = _DS;π _AX = 0x2900;π geninterrupt(0x21);π valid = _AL != 0xFF;π if (*s == 'B' && valid) {π char far *equipbyte = (char far *)0x00400010UL;π valid = (*equipbyte & (3 << 6)) != 0;π }π printf("Drive '%s' is %sa valid drive.\n",π s, valid ? "" : "not ");π }π }ππQ404. How can I make my single floppy drive both a: and b:?ππ Under any DOS since DOS 2.0, you can put the commandππ assign b=aππ into your AUTOEXEC.BAT file. Then, when you type "DIR B:" you'll noπ longer get the annoying prompt to insert diskette B (and the evenπ more annoying prompt to insert A the next time you type "DIR A:").ππ You may be wondering why anybody would want to do this. Suppose youπ use two different machines, maybe one at home and one at work. Oneπ of them has only a 3.5" diskette drive; the other machine has twoπ drives, and b: is the 3.5" one. You're bound to type "dir b:" onπ the first one, and get the nuisance messageππ Insert diskette for drive B: and press any key when ready.ππ But if you assign drive b: to point to a:, you avoid this problem.ππ Caution: there are a few commands, such as DISKCOPY, that will notπ work right on ASSIGNed or SUBSTed drives. See the DOS manual forπ the full list. Before typing one of those commands, be sure to turnπ off the mapping by typing "assign" without arguments.ππ The DOS 5.0 manual says that ASSIGN is obsolete, and recommends theπ equivalent form of SUBST: "subst b: a:\". Unfortunately, if thisπ command is executed when a: doesn't hold a diskette, the commandπ fails. ASSIGN doesn't have this problem, so I must advise you toπ disregard that particular bit of advice in the DOS manual.ππQ405. Why won't my C program open a file with a path?ππ You've probably got something like the following code:ππ char *filename = "c:\foo\bar\mumble.dat";π . . . fopen(filename, "r");ππ The problem is that \f is a form feed, \b is a backspace, and \m isπ m. Whenever you want a backslash in a string constant in C, youπ must use two backslashes:ππ char *filename = "c:\\foo\\bar\\mumble.dat";ππ This is a feature of every C compiler, because Dennis Ritchieπ designed C this way. It's a problem only on MS-DOS systems, becauseπ only DOS (and Atari ST/TT running TOS, I'm told) uses the backslashπ in directory paths. But even in DOS this backslash conventionπ applies _only_ to string constants in your source code. For fileπ and keyboard input at run time, \ is just a normal character, soπ users of your program would type in file specs at run time the sameπ way as in DOS commands, with single backslashes.ππ Another possibility is to code all paths in source programs with /π rather than \ characters:ππ char *filename = "c:/foo/bar/mumble.dat";ππ Ralf Brown writes that "All versions of the DOS kernel accept eitherπ forward or backslashes as directory separators. I tend to use thisπ form more frequently than backslashes since it is easier to type andπ read." This applies to DOS function calls (and therefore to callsπ to the file library of every programming language), but not to DOSπ commands.ππQ406. How can I redirect printer output to a file?ππ My personal favorite utility for this purpose is PRN2FILE from {PCπ Magazine}, available from Simtel as PD1:<MSDOS.PRINTER>PRN2FILE.ARC,π or from garbo as prn2file.zip in /pc/printer. ({PC Magazine} hasπ given copies away as part of its utilities disks, so you may alreadyπ have a copy.)ππ Check the PD1:<MSDOS.PRINTER> directory at Simtel, or /pc/printerπ at garbo, for lots of other printer-redirection utilities.ππQ407. How can my program open more files than DOS's limit of 20?ππ (This is a summary of an article Ralf Brown posted on 8 August 1992.)ππ There are separate limits on files and file handles. For example,π DOS opens three files but five file handles: CON (stdin, stdout,π and stderr), AUX (stdaux), and PRN (stdprn).ππ The limit in FILES= in CONFIG.SYS is a system-wide limit on filesπ opened by all programs (including the three that DOS opens and anyπ opened by TSRs); each process has a limit of 20 handles (includingπ the five that DOS opens). Example: CONFIG.SYS has FILES=40. Thenπ program #1 will be able to open 15 file handles. Assuming that theπ program actually does open 15 handles pointing to 15 differentπ files, other programs could still open a total of 22 files (40-3-15π = 22), though no one program could open more than 15 file handles.ππ If you're running DOS 3.3 or later, you can increase the per-processπ limit of 20 file handles by a call to INT 21 function 67, Set Handleπ Count. Your program is still limited by the system-wide limit onπ open files, so you may also need to increase the FILES= value inπ your CONFIG.SYS file (and reboot). The run-time library that you'reπ using may have a fixed-size table of file handles, so you may alsoπ need to get source code for the module that contains the table,π increase the table size, and recompile it.ππQ408. How can I read, create, change, or delete the volume label?ππ In DOS 5.0 (and, I believe, in 4.0 as well), there are actually twoπ volume labels: one, the traditional one, is an entry in the rootπ directory of the disk; and the other is in the boot record alongπ with the serial number (see next Q). The DIR and VOL commandsπ report the traditional label; the LABEL command reports theπ traditional one but changes both of them.ππ In DOS 4.0 and later, use INT 21 function 69 to access the bootπ record's serial number and volume label together; see the next Q.ππ Assume that by "volume label" you mean the traditional one, the oneπ that DIR and VOL display. Though it's a directory entry in the rootπ directory, you can't change it using the newer DOS file-accessπ functions (3C, 41, 43); instead, use the old FCB-oriented directoryπ functions. Specifically, you need to allocate a 64-byte buffer andπ a 41- byte extended FCB (file control block). Call INT 21 AH=1A toπ find out whether there is a volume label. If there is, AL returns 0π and you can change the label using DOS function 17 or delete itπ using DOS function 13. If there's no volume label, function 1A willπ return FF and you can create a label via function 16. Importantπ points to notice are that ? wildcards are allowed but * are not; theπ volume label must be space filled not null terminated.ππ The following MSC 7.0 code worked for me in DOS 5.0; the functionsπ it uses have been around since DOS 2.0. The function parameter is 0π for the current disk, 1 for a:, 2 for b:, etc. It doesn't matterπ what your current directory is; these functions always search theπ root directory for volume labels. (I didn't try to change theπ volume label of any networked drives.)ππ // Requires DOS.H, STDIO.H, STRING.Hπ void vollabel(unsigned char drivenum) {π static unsigned char extfcb[41], dta[64], status, *newlabel;π int chars_got = 0;π #define DOS(buff,func) __asm { __asm mov dx,offset buff \π __asm mov ax,seg buff __asm push ds __asm mov ds,ax \π __asm mov ah,func __asm int 21h __asm pop ds \π __asm mov status,al }π #define getlabel(buff,prompt) newlabel = buff; \π memset(newlabel,' ',11); printf(prompt); \π scanf("%11[^\n]%n", newlabel, &chars_got); \π if (chars_got < 11) newlabel[chars_got] = ' ';ππ // Set up the 64-byte transfer area used by function 1A.π DOS(dta, 1Ah)π // Set up an extended FCB and search for the volume label.π memset(extfcb, 0, sizeof extfcb);π extfcb[0] = 0xFF; // denotes extended FCBπ extfcb[6] = 8; // volume-label attribute bitπ extfcb[7] = drivenum; // 1=A, 2=B, etc.; 0=current driveπ memset(&extfcb[8], '?', 11); // wildcard *.*π DOS(extfcb,11h)π if (status == 0) { // DTA contains volume label's FCBπ printf("volume label is %11.11s\n", &dta[8]);π getlabel(&dta[0x18], "new label (\"delete\" to delete): ");π if (chars_got == 0)π printf("label not changed\n");π else if (strncmp(newlabel,"delete ",11) == 0) {π DOS(dta,13h)π printf(status ? "label failed\n" : "label deleted\n");π }π else { // user wants to change labelπ DOS(dta,17h)π printf(status ? "label failed\n" : "label changed\n");π }π }π else { // no volume label was foundπ printf("disk has no volume label.\n");π getlabel(&extfcb[8], "new label (<Enter> for none): ");π if (chars_got > 0) {π DOS(extfcb,16h)π printf(status ? "label failed\n" : "label created\n");π }π }π } // end function vollabelππQ409. How can I get the disk serial number?ππ Use INT 21. AX=6900 gets the serial number; AX=6901 sets it. Seeπ Ralf Brown's interrupt list, or page 496 of the July 1992 {PCπ Magazine}, for details.ππ This function also gets and sets the volume label, but it's theπ volume label in the boot record, not the volume label that a DIRπ command displays. See the preceding Q.ππQ410. What's the format of .OBJ, .EXE., .COM files?ππ Please see section 2, "Compile and link".πππsection 5. Serial ports (COM ports)π===================================ππQ501. How do I set my machine up to use COM3 and COM4?ππ Unless your machine is fairly old, it's probably already set up.π After installing the board that contains the extra COM port(s),π check the I/O addresses in word 0040:0004 or 0040:0006. (In DEBUG,π type "D 40:4 L4" and remember that every word is displayed lowπ byte first, so if you see "03 56" the word is 5603.) If thoseπ addresses are nonzero, your PC is ready to use the ports and youπ don't need the rest of this answer.ππ If the I/O address words in the 0040 segment are zero after you'veπ installed the I/O board, you need some code to store these valuesπ into the BIOS data segment:ππ 0040:0004 word I/O address of COM3π 0040:0006 word I/O address of COM4π 0040:0011 byte (bits 3-1): number of serial ports installedππ The documentation with your I/O board should tell you the portπ addresses. When you know the proper port addresses, you can addπ code to your program to store them and the number of serial portsπ into the BIOS data area before you open communications. Or you canπ use DEBUG to create a little program to include in your AUTOEXEC.BATπ file, using this script:ππ n SET_ADDR.COM <--- or a different name ending in .COMπ a 100π mov AX,0040π mov DS,AXπ mov wo [0004],aaaa <--- replace aaaa with COM3 address or 0π mov wo [0006],ffff <--- replace ffff with COM4 address or 0π and by [0011],f1π or by [0011],8 <--- use number of serial ports times 2π mov AH,0π int 21π <--- this line must be blankπ rCXπ 1fπ rBXπ 0π wπ qππQ502. How do I find the I/O address of a COM port?ππ Look in the four words beginning at 0040:0000 for COM1 through COM4.π (The DEBUG command "D 40:0 L8" will do this. Remember that wordsπ are stored and displayed low byte first, so a word value of 03F8π will be displayed as F8 03.) If the value is zero, that COM port isπ not installed (or you've got an old BIOS; see the preceding Q). Ifπ the value is nonzero, it is the I/O address of the transmit/receiveπ register for the COM port. Each COM port occupies eight consecutiveπ I/O addresses (though only seven are used by many chips).ππ Here's some C code to find the I/O address:ππ unsigned ptSel(unsigned comport) {π unsigned io_addr;π if (comport >= 1 && comport <= 4) {π unsigned far *com_addr = (unsigned far *)0x00400000UL;π io_addr = com_addr[comport-1];π }π elseπ io_addr = 0;π return io_addr;π }ππQ503. But aren't the COM ports always at I/O addresses 3F8, 2F8, 3E8,π and 2E8?ππ The first two are usually right (though not always); the last twoπ are different on many machines.ππQ504. How do I configure a COM port and use it to transmit data?ππ After hearing several recommendations, I looked at Joe Campbell's {Cπ Programmer's Guide to Serial Communications}, ISBN 0-672-22584-0,π and agree that it is excellent. He gives complete details on howπ serial ports work, along with complete programs for doing polled orπ interrupt-driver I/O. The book is quite thick, and none of it looksπ like filler.ππ If Campbell's book is overkill for you, you'll find a good shortπ description of serial I/O in {DOS 5: A Developer's Guide}, ISBNπ 1-55851-177-6, by Al Williams.ππ You may also want to look at an extended example in Borland'sπ TechFax TI445, part of PD1:<MSDOS.TURBO-C> at Simtel. Thoughπ written by Borland, much of it is applicable to other forms of C,π and it should give you ideas for other programming languages.ππsection 6. Other hardware questions and problemsπ================================================ππQ601. Which 80x86 CPU is running my program?ππ According to an article posted by Michael Davidson, Intel's approvedπ code for distinguishing among 8086, 80286, 80386, and 80486 and forπ detecting the presence of an 80287 or 80387 is published in theπ Intel's 486SX processor manual (order number 240950-001). You canπ download David Kirschbaum's improved version of this from Simtel asπ PD1:<MSDOS.SYSUTL>CPUID593.ZIP.ππ According to an article posted by its author, WCPU041.ZIP knows theπ differences between DX and SX varieties of 386 and 486 chips, andπ can also detect a math coprocessor. It's in PD1:<MSDOS.SYSUTL> atπ Simtel.ππQ602. How can a C program send control codes to my printer?ππ If you just fprintf(stdprn, ...), C will translate some of yourπ control codes. The way around this is to reopen the printer inπ binary mode:ππ prn = fopen("PRN", "wb");ππ You must use a different file handle because stdprn isn't an lvalue.π By the way, PRN or LPT1 must not be followed by a colon in DOS 5.0.ππ There's one special case, Ctrl-Z (ASCII 26), the DOS end-of-fileπ character. If you try to send an ASCII 26 to your printer, DOSπ simply ignores it. To get around this, you need to reset theπ printer from "cooked" to "raw" mode. Microsoft C users must use intπ 21 function 44, "get/set device information". Turbo C and Borlandπ C++ users can use ioctl to accomplish the same thing:ππ ioctl(fileno(prn), 1, ioctl(fileno(prn),0) & 0xFF | 0x20, 0);ππ An alternative approach is simply to write the printer output into aπ disk file, then copy the file to the printer with the /B switch.ππ A third approach is to bypass DOS functions entirely and use theπ BIOS printer functions at INT 17. If you also fprintf(stdprn,...)π in the same program, you'll need to use fflush( ) to synchronizeπ fprintf( )'s buffered output with the BIOS's unbuffered.ππ By the way, if you've opened the printer in binary mode from a Cπ program, remember that outgoing \n won't be translated to carriageπ return/line feed. Depending on your printer, you may need to sendπ explicit \n\r sequences.ππQ603. How can I redirect printer output to a file?ππ Please see section 4, "Disks and files", for the answer.ππQ604. Which video adapter is installed?ππ The technique below should work if your BIOS is not too old. Itπ uses three functions from INT 10, the BIOS video interrupt. (Ifπ you're using a Borland language, you may not have to do this theπ hard way. Look for a function called DetectGraph or somethingπ similar.)ππ Set AH=12h, AL=0, BL=32h; INT 10h. If AL is 12h, you have a VGA.π If not, set AH=12h, BL=10h; INT 10h. If BL is 0,1,2,3, you have anπ EGA with 64,128,192,256K memory. If not, set AH=0Fh; INT 10h. Ifπ AL is 7, you have an MDA (original monochrome adapter) or Hercules;π if not, you have a CGA.ππ I've tested this for my VGA and got the right answer; but I can'tπ test it for the other equipment types. Please let me know by emailπ at brown@ncoast.org if your results vary.ππQ605. How do I switch to 43- or 50-line mode?ππ Download PD1:<MSDOS.SCREEN>VIDMODE.ZIP from Simtel or one of theπ mirror sites. It contains .COM utilities and .ASM source code.ππQ606. How can I find the Microsoft mouse position and button status?ππ Use INT 33 function 3, described in Ralf Brown's interrupt list.ππ The Windows manual says that the Logitech mouse is compatible withπ the Microsoft one, so I assume the interrupt will work the same.ππ Also, see the directory PD1:<MSDOS.MOUSE> at Simtel.ππQ607. How can I access a specific address in the PC's memory?ππ First check the library that came with your compiler. Many vendorsπ have some variant of peek and poke functions; in Turbo Pascal useπ the pseudo-arrays Mem, MemW, and MemL. As an alternative, you canπ construct a far pointer: use Ptr in Turbo Pascal, MK_FP in theπ Turbo C family, and FP_OFF and FP_SEG in Microsoft C.ππ Caution: Turbo C and Turbo C++ also have FP_OFF and FP_SEG macros,π but they can't be used to construct a pointer. In Borland C++ thoseπ macros work the same as in Microsoft C, but MK_FP is easier to use.ππ By the way, it's not useful to talk about "portable" ways to doπ this. Any operation that is tied to a specific memory address isπ not likely to work on another kind of machine.ππQ608. How can I read or write my PC's CMOS memory?ππ There are a great many public-domain utilities that do this. Theseπ were available for download from Simtel as of 31 March 1992:ππ PD1:<MSDOS.AT>π CMOS14.ZIP 5965 920817 Saves/restores CMOS to/from fileπ CMOSER11.ZIP 28323 910721 386/286 enhanced CMOS setup programπ CMOSRAM.ZIP 76096 920214 Save AT/386/486 CMOS data to file and restoreπ ROM2.ARC 20497 900131 Save AT and 386 CMOS data to file and restoreπ SETUP21.ARC 24888 880613 Setup program which modifies CMOS RAMπ VIEWCMOS.ARC 15374 900225 Display contents of AT CMOS RAM, w/C sourceππ At garbo, /pc/ts/tsutle17.zip contains a CMOS program to check andπ display CMOS memory, but not to write to it.ππ I have heard good reports of CMOS299.ZIP, available in the pc.dirπ directory of cantva.canterbury.ac.nz [132.181.30.3].ππ Of the above, my only experience is with CMOSRAM, which seems toπ work fine. It contains an excellent (and witty) .DOC file thatπ explains the hardware involved and gives specific recommendationsπ for preventing disaster or recovering from it. It's $5 shareware.ππ Robert Jourdain's {Programmer's Problem Solver for the IBM PC, XT,π and AT} has code for accessing the CMOS RAM, according to an articleπ posted in this newsgroup.ππQ609. How can I access memory beyond 640K?ππ I'm outside my expertise on this one, but in late 1992 Jamshidπ Afshar (jamshid@emx.utexas.edu) kindly supplied the following, whichπ incorporates some corrections agreed with Duncan Murdoch (dmurdoch@π mast.queensu.ca). If you have any corrections or comments, pleaseπ send them to both the above addresses.ππ ...........................(begin quote)............................π 1. Use XMS or EMS memory. XMS is preferable in most cases, butπ some machines won't provide it. There are some libraries availableπ at Simtel to access XMS or EMS. The disadvantage is that youπ don't allocate the memory as you would with malloc() (or `new' inπ C++). I believe it also requires that you lock this memory when inπ use. This means your code is not easily ported to other (andπ future) operating systems and that your code is more convoluted thanπ it would be under a "real" os. The advantage is that the libraryπ works with compilers since Turbo C 2.0 (I think) and that yourπ program will easily run on even 286s.ππ 2. Program under MS Windows. MS Windows functions as a 16-bit DOSπ Extender (see #3). Borland/Turbo C++ 3.x includes EasyWin [andπ Microsoft C/C++ 7.0 has QuickWin --ed.] which is a library thatπ automatically lets you compile your current code using C/C++π standard input or <conio.h> into a MS Windows program so your codeπ can immediately allocate many MBs of memory (Windows enhanced modeπ even does virtual memory). The disadvantage is that like any 16-bitπ Extender a single malloc() is restricted to 64K (unless you want toπ mess with huge pointers in Windows). Also, EasyWin's screen outputπ is significantly slower than a DOS character-mode program's and youπ must of course run the program from Windows.ππ 3. Use a 16-bit or 32-bit DOS Extender. This is definitely theπ best solution from the programmer's standpoint. You just allocateπ as much memory as you need using malloc() or 'new'. A 16-bitπ Extender still has 16-bit ints and restricts arrays to 64K, but aπ 32-bit Extender has 32-bits ints (which makes porting a lot of UNIXπ code easier) so there are no 64K limits. A 32-bit Extender requiresπ a 32-bit compiler and the program will not run on 286s. Someπ Extenders also do virtual memory. Using an Extender doesn't requireπ source code changes and unlike option #1 your code is portable andπ not obsolete in a few months. Your options for this solution are:ππ - Buy PharLap's 16-bit Extender that works with BC++ 3.0+ and MSCπ (just requires a relink). Note, the BC++ 3.1 upgrade came withπ PharLap "lite". Pharlap's 32-bit Extender works with 32-bitπ compilers like [?]ππ - Get the GNU (free,copylefted) gcc 2.x compiler which DJ Delorieπ ported from UNIX and which uses his 32-bit Extender. It supportsπ C and C++, but the Extender is VCPI which means neither theπ compiler nor programs it produces will run in a DOS session underπ Windows. FTP to barnacle.erc.clarkson.edu and getπ pub/msdos/djgpp/readme.ππ - Get a 32-bit compiler or one that comes with a DOS Extender.π Zortech comes with 16-bit and a 32-bit Extenders (no debugger forπ 32-bit programs, but Flashtek sells one). Watcom also makes a Cπ [and C++?] 32-bit compiler. [If anyone else has products or plansπ to announce, please let me know.]ππ - Buy Borland Pascal 7.0. It includes a 16 bit royalty-free DOSπ extender using the same interface as MS Windows. It functionsπ under a DPMI server like Windows or QDPMI from Quarterdeck, andπ also provides its own server which you can distribute with yourπ programs.ππ 4. This option doesn't really count since it's not a solution inπ DOS, but you could switch to a full 32-bit operating system likeπ OS/2 2.0 or UNIX (or NT when it comes out). I believe Win32 willπ allow you to write 32-bit Windows programs. [can someone fill me inπ on what exactly Win32 is?]π ............................(end quote).............................πππsection 7. Other software questions and problemsπ================================================ππQ701. How can a program reboot my PC?ππ You can generate a "cold" boot or a "warm" boot. A cold boot isπ the same as turning the power off and on; a warm boot is the same asπ Ctrl-Alt-Del and skips the power-on self test.ππ For a warm boot, store the hex value 1234 in the word at 0040:0072.π For a cold boot, store 0 in that word. Then, if you want to liveπ dangerously, jump to address FFFF:0000. Here's C code to do it:ππ /* WARNING: data loss possible */π void bootme(int want_warm) /* arg 0 = cold boot, 1 = warm */ {π void (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;π unsigned far* type = (unsigned far*)0x00400072UL;π *type = (want_warm ? 0x1234 : 0);π (*boot)( );π }ππ What's wrong with that method? It will boot right away, withoutπ closing files, flushing disk caches, etc. If you boot withoutπ flushing a write-behind disk cache (if one is running), you couldπ lose data or even trash your hard drive.ππ There are two methods of signaling the cache to flush its buffers:π (1) simulate a keyboard Ctrl-Alt-Del in the keystroke translationπ function of the BIOS (INT 15 function 4F), and (2) issue a diskπ reset (DOS function 0D). Most disk-cache programs hook one or bothπ of those interrupts, so if you use both methods you'll probably beπ safe.ππ When user code simulates a Ctrl-Alt-Del, one or more of the programsπ that have hooked INT 15 function 4F can ask that the key be ignored byπ clearing the carry flag. For example, HyperDisk does this when itπ has started but not finished a cache flush. So if the carry flagπ comes back cleared, the boot code has to wait a couple of cluckπ ticks and then try again. (None of this matters on older machinesπ whose BIOS can't support 101- or 102-key keyboards; see "What is theπ SysRq key for?" in section 3, "Keyboard".)ππ Here's C code that tries to signal the disk cache (if any) to flush:ππ #include <dos.h>π void bootme(int want_warm) /* arg 0 = cold boot, 1 = warm */ {π union REGS reg;π void (far* boot)(void) = (void (far*)(void))0xFFFF0000UL;π unsigned far* boottype = (unsigned far*)0x00400072UL;π char far* shiftstate = (char far*)0x00400017UL;π unsigned ticks;π int time_to_waste;π /* Simulate reception of Ctrl-Alt-Del: */π for (;;) {π *shiftstate |= 0x0C; /* turn on Ctrl & Alt */π reg.x.ax = 0x4F53; /* 0x53 = Del's scan code */π reg.x.cflag = 1; /* sentinel for ignoring key */π int86(0x15, ®, ®);π /* If carry flag is still set, we've finished. */π if (reg.x.cflag)π break;π /* Else waste some time before trying again: */π reg.h.ah = 0;π int86(0x1A, ®, ®);/* system time into CX:DX */π ticks = reg.x.dx;π for (time_to_waste = 3; time_to_waste > 0; ) {π reg.h.ah = 0;π int86(0x1A, ®, ®);π if (ticks != reg.x.dx)π ticks = reg.x.dx , --time_to_waste;π }π }π /* Issue a DOS disk reset request: */π reg.h.ah = 0x0D;π int86(0x21, ®, ®);π /* Set boot type and boot: */π *boottype = (want_warm ? 0x1234 : 0);π (*boot)( );π }ππQ702. How can I time events with finer resolution than the systemπ clock's 55 ms (about 18 ticks a second)?ππ The following files, among others, can be downloaded from Simtel:ππ PD1:<MSDOS.AT>π ATIM.ARC 5946 881126 Precision program timing for ATππ PD1:<MSDOS.C>π MILLISEC.ZIP 37734 911205 MSC/asm src for millisecond res timingπ MSCHRT3.ZIP 53708 910605 High-res timer toolbox for MSC 5.1π MSEC_12.ZIP 8484 920320 High-def millisec timer v1.2 (C,ASM)π ZTIMER11.ZIP 77625 920428 Microsecond timer for C, C++, ASMππ PD1:<MSDOS.TURBO-C>π TCHRT3.ZIP 53436 910606 High-res timer toolbox for Turbo C 2.0π TCTIMER.ARC 20087 891030 High-res timing of events for Turbo Cππ PD1:<MSDOS.TURBOPAS>π BONUS507.ARC 150435 900205 [Turbo Pascal source: high-res timing]ππ Pascal users can download source code in /pc/turbopas/bonus507.zipπ at garbo.ππQ703. How can I find the error level of the previous program?ππ First, which previou 3 05-28-9313:45ALL SWAG SUPPORT TEAM Strings, Compiler, OutputIMPORT 28 PASCAL.FAQ Frequently asked questions about PascalππThe aim of this document is to give answers to frequently askedπquestions in the pascal echo. Thumb rules before asking for help are toπlook in the manuals and in the online help first. Many problems can beπsolved by just looking into either / both of them. Here are someπtopics, that come very often in the Pascal Echo.ππ Part Iπ #1: Changing the case of stringsπ #2: Compiler errorsπ #3: Redirection of outputππ---------------------------------------------------------------------π #1 StringsππQ1: How do I access a single char in a string ?πQ2: How can I make a string all upper cases ?ππA1: A string is an Array[0..255] of Char, where the 0th char is theπ length of the string. To access any character in the string, you canπ writeππ MyChar := String[ I ];ππA2: To map a single character to uppercase, you can use the UpCase()π function of the run time library. To turn a whole string into upperπ cases, just use this function :ππFunction UpperCase( const S : String ) : String;πVar I : Integer;πBeginπ { first store the length in the result }π UpperCase[ 0 ] := S[ 0 ]π { now translate each char in S into a upper case char in UpperCase }π For I := 1 to Length( S ) doπ UpperCase[ I ] := UpCase( S[ I ]);πEnd;ππThere is a assembler implementation in the manuals ( the linkingπassembly language chaprt ), and there are many other optimized upperπcase routines out there.ππ---------------------------------------------------------------------π #2 Compiler ErrorsππQ1: I get a "Code segment too large" error. How can I fix it ?πQ2: I get a "Data segment too large" error. How can I fix it ?ππA1: This error means, that you have more than 64K code in one partπ of your program. To reduce the size of this code segment, you needπ to move parts of it into other units. This is possible to virtuallyπ unlimited units.ππA2: This error means, that you have more than 64K data in your program.π You need to put some data on the heap ( -> GetMem / FreeMem / Newπ / Dispose ) or to reduce your global data and make it local data onπ the stack.ππ---------------------------------------------------------------------π #3 Redirection of outputππQ1: How can I make the output of my program redirectable under DOS ?ππA1: In general, the output of TP programs _is_ redirectable, except ifπ you use the CRT unit. Then you need to either reassign output toπ '' or to declare a Text variable called for example ReOutput ( forπ Redirectable Output ), and write the output to it.ππExample :ππUses CRT;ππBeginπ WriteLn( 'This will always show up. Just a copyright.' );π Assign( Output, '' );π Rewrite( Output );π WriteLn( 'This is redirectable.' );π AssignCRT( Output );π Rewrite( Output );π WriteLn( 'And this will alyways show up again.' );πEnd.ππThere are some myths that setting DirectVideo to False would result inπredirectable output even when using CRT, or that TP _always_ writesπdirectly to the screen, and that TP output is _never_ redirectable. Youπcan ignore thos myths, TP writes to the screen using DOS, _except_ ifπyou use the unit CRT. Then TP writes directly to the screen. If you setπthe variable DirectVideo to False, TP uses BIOS calls to write to theπscreen.π 4 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND - LINKER QA IMPORT 7 πTP 5.0 5.5 - LINKER ELIMINATES UNUSED DATAπQ. Does the built-in linker eliminate unused data?πA. Yes. Unused code AND data are stripped when you compile toπ disk. However, if more than one variable is defined in theπ same VAR block and any one is used, the others will not beπ stripped from the .EXE. For example:ππ var A, B: integer;π var C: integer;π beginπ A:= 0;π end.ππ In this example, although variable B is never used, it wasπ defined in the same block as a variable A. Therefore, B willπ not be linked out. Variable C will be removed from the .EXE asπ it is not used and is not in the same VAR block.ππ 5 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND - MAC Pascal QA IMPORT 9 πTP / TPMAC - PC / MACINTOSH PORTABILITYπQ. How portable is the PC version of Turbo Pascal to the π Macintosh?πA. Any Portion of the program that is Standard Pascal will port π over nicely. However, the PC and the Macintosh are twoπ completely different machines and any PC or Macintoshπ specific code will have to be rewritten. PC programmers willπ generally want to rewrite the user interface so that isπ similar to what Macintosh users expect to see on a seriousπ Macintosh application.πππTPMAC 1.0 1.1 - PASPRINTER PRINT WITHIN PROGRAMπQ. How do I print from within a program?πA. Uses PasPrinter and Writeln(Printer, data);ππTPMAC 1.0 1.1 READLN WRITELN - THE MISSING SUPPORT UNIT ERRORπQ. I get a runtime error message saying that the support unitπ is missing when I try to use a Readln or Writeln.πA. Take out the {$U-} directive. It causes the PasConsole andπ PasInOut units to not link in with the program and units.ππ 6 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND - MEMORY QA IMPORT 16 ππTITLE: TURBO PASCAL MEMORY ISSUESπ===========================================================ππTP 4.0 5.0 5.5 EXEC - SETTING HEAP MEMORYπQ. How do I set the heap memory when executing a child process?πA. Probably the best way to do this is through trial and error. π Set the heap maximum to some large value, and run the program.π If it runs out of memory, then divide the maximum by 2, and soπ on, until the parent and child programs have enough memory. π The same can be done with the heap minimum. Also, if you haveπ access to CompuServe, you can download a routine that swapsπ the parent program between memory and disk.ππTP 4.0 5.0 5.5 - ALLOCATING AND RECLAIMING MEMORY USING DOS πQ. What Dos functions can I use to reclaim memory to Dos? πA. You essentially must write your own GetMem and FreeMemπ routines. GetMem would make a call to Dos function $48 andπ FreeMem would make a call to Dos function $49. For moreπ details on these functions please consult an IBM PCπ programmer's guide.ππTP 4.0 5.0 5.5 - MEMORY CONTROL BLOCKSπQ. How many memory control blocks does a Turbo Pascal programπ use?πA. Just one.πππTP 5.0 5.5 - EMS SUPPORTπQ. Does Turbo Pascal 5.0+ support EMS?πA. Yes. Turbo Pascal 5.0+ will use up to 64K (if selected inπ TINST) of EMS for storing the edit buffer. In addition, youπ can instruct the Overlay unit to place your overlaid units onπ EMS. Finally, EMS.PAS on the distribution disk shows you howπ to access EMS memory.πππTP 5.5 - HEAP SPACE STILL CREATED WITH HEAP MAX 0πQ. Compiling to disk with my heap maximum set to 0 still createsπ heap space when running .EXE file. Why?πA. This has to do with the way DOS allocates memory for .EXEπ files in chunks of 512 bytes, rather than in chunks of 16π bytes. As a result of DOS's allocation, you always get 0-496π bytes more than you request.ππ 7 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND Mixed Lang QA IMPORT 17 TP 4.0 5.0 5.5 - LINKING TURBO C OR ASSEMBLER .OBJ OBJECT FILESπQ. Are the .OBJ files generated by Turbo C and Turboπ Assembler compatible with 4.0+?πA. Yes. You can write Turbo C or Turbo Assembler routines andπ link the .OBJ files into your Turbo Pascal programs by usingπ the {$L} compiler directive. See the CTOPAS example on theπ distribution diskette.ππTP 4.0 5.0 5.5 - OBJECT FILE CREATIONπQ. Does Turbo Pascal create object files that can be linked intoπ other languages?πA. Turbo Pascal 4.0+ generates .TPU (Turbo Pascal Unit) files, notπ .OBJ files. We've made that decision for many reasons: π π 1. TP 4.0+'s .TPU files are smaller than .OBJ's, and theyπ contain symbolic information important to the support ofπ Pascal's strict type conventions (types, constants,π etc.). π 2. .TPU files allow "smart linking" - elimination of unusedπ code and data on a procedure-by-procedure basis. π 3. .TPU's allow built-in project management through versionπ 4.0+'s Make and Build commands. π 4. .TPU's allow faster compilation speeds (34,000 lines perπ minute on a PS/2 Model 60).ππTP 4.0 5.0 5.5 - LINKING .OBJ OBJECT FILES FROM OTHER ASSEMBLERSπQ. Will the $L compiler directive work for compiler object filesπ other than assembler?πA. That depends on the language. TURBO requires all the codeπ in the .OBJ to be in *one* CODE segment, and all the data toπ be in *one* DATA segment. With assembly language that's easy,π but it may not work with some high-level language compilers.π You can use Turbo C to generate .OBJ files for use by Turboπ Pascal programs. An example, CPASDEMO.PAS is included on theπ distribution disks.ππTP 4.0 5.0 5.5 - INTERFACING MODULES WRITTEN IN MICROSOFT CπQ. Can I link modules, written in Microsoft C, with Turbo Pascalπ programs?πA. Yes. M/S C .OBJ modules can be linked provided they do notπ use the C runtime library. The same limitations apply to M/Sπ C modules as to Turbo C modules.ππ 8 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND - OOP QA IMPORT 56 πTP 5.5 OOP - OBJECT EXE FILE SIZE OVERHEADπQ. How much overhead will result in the *.EXE file from using theπ object oriented style?πA. The overhead will result from the pointer from the object toπ its method. This is a 4 byte pointer, so there isn't thatπ much extra code generated.πππTP 5.5 OOP - PROTECTED AND PRIVATE FIELDSπQ. Does Turbo Pascal 5.5 support Protected or Private fields?πA. No it does not.πππTP 5.5 OOP - RECORDS VS. OBJECTSπQ. What things can be done with Records that can not be done withπ Objects?πA. You cannot have:ππ 1. Variant Objects.π 2. Objects with absolutes.π 3. Directly nested Objects.ππ You can have:ππ 1. Pointers to objects.ππTP 5.5 OOP - EXTERNAL METHODSπQ. Can methods within an object be external?πA. Yes. Virtual and Static methods can be written as externalπ code. There is no difference between an external Virtual orπ Static method. External Constructors and Destructors areπ difficult to write due to the Prolog and Epilog code withinπ them.ππTP 5.5 OOP - CONSTRUCTOR USEπQ. What are the three purposes of a Constructor?πA. 1. Insert the address of the VMT into the Object variable.π (Implicit)π 2. To allocate memory for the Object variable. π (Implicit)π 3. To initialize the Object variable. π (Explicit)ππTP 5.5 OOP - DESTRUCTOR HEAP CORRUPTIONπQ. Why is my destructor fragging my heap?πA. Use: π π Dispose(ptr,done);ππ instead of:π π ptr^.done;π Dispose(done);ππTP 5.5 OOP - SAVING OBJECTS TO DISKπQ. Can I save my objects to a disk file like I can a recordπ structure?πA. We have provided an example program on the distributionπ diskette, STREAMS, which documents how to save an object toπ disk.ππTP 5.5 OOP - FILES OF OBJECT TYPEπQ. Why can't I make a file of ObjectType?πA. Because by the rules of polymorphism, any descendant ofπ ObjectType would be type compatible and be able to be written π to disk as well. The problem with this is that the descendantsπ may be (and usually are) of a larger size than the ObjectType π itself. Pascal's file structure require all records to be π the same size, otherwise, how do you know which size object π do you read in? For an example on how to do Object disk I/O, π please see the STREAMS example on the distribution diskettes.ππTP 5.5 OOP - SIZEOF OBJECTS CONSTRUCTORπQ. Why does Sizeof(MyObject) return a size 2 bytes larger than Iπ expect when no virtual methods are used?πA. By placing a constructor in your object, you're making the π object virtual.ππTP 5.5 OOP - LINKER STRIPS UNUSED STATIC METHODSπQ. It appears that my static methods are getting stripped from π my program by the smart linker when they are unused. Yet, π my virtual methods are not. How come?πA. Static methods can be stripped because it can be determined atπ link time what methods will be called. Virtual methods can π not be stripped because the program will not know what methodsπ will be used, and what will not, until run time. This is π because of late binding.ππTP 5.5 OOP - CONSTRUCTOR CALL TO ANCESTOR WITH VIRTUALSπQ. If a descendant of a virtual object defines no virtualπ methods of its own, does it need to call the ancestor'sπ constructor?πA. If an object is a descendant of a virtual object, it must callπ that ancestor's constructor or it's own constructor. Even ifπ the new object does not define any virtuals of its own. Forπ example:ππ Typeπ A = Object π Constructor Init; π Procedure AA; Virtual; π End; π B = Object ( A ) π End; ππ For each instance of A and of B, Init must be called or theπ Virtual Method Table pointer will not be loaded correctly.ππTP 5.5 OOP - OVERRIDE VIRTUAL METHOD CALLING ANCESTORπQ. Can I override a virtual method and force a call to theπ ancestor objects method?πA. No. Late binding will always call the current method and itπ defeats the purpose of object oriented program to go around π this feature.ππTP 5.5 OOP - CONSTRUCTOR CALL WITHIN METHODπQ. I am calling my constructor from within a method, why am Iπ having problems?πA. The problem will arise when the constructor loads the new VMTπ pointer. It loads the pointer to the VMT for the constructor'sπ table, not the instances. Therefore if a descendant calls anπ ancestor's constructor, the descendant's VMT will now point toπ the ancestors VMT. The problem now occurs when the descendantπ tries to call a method that was defined after the ancestor.π The VMT entry for this method is unknown. Look at theπ following example: ππ Type π L1 = Object π Constructor Init;π Procedure First; Virtual; π End; π L2 = Object ( L1 ); π Constructor Init; π Procedure Second; Virtual; π End;ππ Constructor L1.Init;π Beginπ End;ππ Constructor L2.Init;π Beginπ End;ππ Procedure L1.First;π Beginπ Init;π End;ππ Procedure L2.Second;π Beginπ Init;π End;ππ Varπ L : L2;ππ Beginπ L.Init; { This calls L2.Init and loads a pointer to }π { the L2 VMT into L. }π L.First; { This will call L1.First, which in turn calls }π { L1.Init because as far as the procedure is }π { concerned, the Self pointer is a pointer }π { to an object of type L1. }π L.Second; { This is undefined. Since the VMT now }π { pointed to by L is L1's, the pointer to } π { method Second is undefined. Therefore, the }π { call to this method is undefined. }π ...ππTP 5.5 OOP - CONSTRUCTOR CALL WITHIN POLYMORPHIC METHODπQ. Does the previous question apply to polymorphic procedures?πA. Yes. The previous question and answer apply to every caseπ where the compiler may think of an object as its ancestor. Aπ polymorphic example that is incorrect follows:ππ Procedure Init ( var x : L1 );π Beginπ x.Init; { This will ALWAYS call L1.Init }π End;ππTP 5.5 OOP - CONSTRUCTOR CALLING ANCESTOR'S CONSTRUCTORπQ. Should my current object's constructor call it's ancestor'sπ constructors?πA. As a rule of thumb, this is the correct thing to do. It isπ okay to not do it, but it does allow initialization ofπ whatever the previous constructors did.ππTP 5.5 OOP - CALLING ANCESTORS CONSTRUCTORπQ. How do you call a constructor from an ancestor's object?πA. You can call an ancestor's constructor directly from withinπ the constructor of the current object. For example:ππ Typeπ Type1 = Objectπ Constructor Init;π End;π Type2 = Object ( Type1 )π Constructor Init;π End;ππ Constructor Type1.Init;π Beginπ End;ππ Constructor Type2.Init;π Beginπ Type1.Init;π End;π ...ππ 9 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND OVERLAYS QA IMPORT 21 ππTITLE: TRUBO PASCAL OVERLAY ISSUESπ===========================================================ππTP 5.0 5.5 - OVERLAY SUPPORTπQ. Are overlays supported in 5.0+?πA. Yes! See the example program OVRDEMO.PAS and refer to theπ Turbo Pascal manual for information on overlays.ππTP 5.0 5.5 - OVERLAY UNITS LOADED INTO BUFFERπQ. Is there any way to determine what overlay units are loadedπ into the overlay buffer?πA. Using Turbo Pascal 5.0+, there is no defined method forπ determining which units are loaded into the overlay bufferπ area. ππTP 5.0 5.5 - OVERLAYS *.OVR FILESπQ. How can I transfer a large overlay file onto a floppy disk?πA. If the file does not fit on a single density disk, thenπ transfer it to a double density disk. In the latter case,π your application will run only from the double density disk orπ a hard disk.ππTP5.5 OVERLAYπQ. How do I reclaim the memory used by overlay buffer in TP5.5?ππA. The following example demonstrates how to do so.ππunit Marker;ππinterfaceππprocedure RestoreHeap; procedure RestoreOverlay;ππimplementationππvar OldHeapPtr,OldHeapOrg,Temp:pointer;ππvar OldHeapPtr,OldHeapOrg,Temp:pointer;ππprocedure RestoreHeap; beginπ Release(Temp); {2. Release all dynamic variables}π OldHeapOrg:=HeapOrg; {3. Save Current Heap state }π OldHeapPtr:=HeapPtr;π HeapOrg:=ptr(OvrHeapOrg,$0); {4. Set Heap to Origin of Overlay Buffer}π HeapPtr:=HeapOrg;π Mark(Temp); {5. Mark the origin of this heap } end;ππprocedure RestoreOverlay; beginπ Release(Temp); {6. Release all dynamic variables }π HeapOrg:=OldHeapOrg; {7. Restore heap pointers }π HeapPtr:=OldHeapPtr; end;ππbeginπ mark(temp); {1. Mark the beginning of heap after overlay buffer} end.πend.ππThe unit is to be used before any unit that places items into the heap duringπtheir initialization. You would call RESTOREHEAP before using the dynamicπvariables requiring memory used by the overlay buffer. A call to OVRCLEARBUFπmust come before any call to RESTOREHEAP to ensure that the overlay buffer πis empty and to force a reload of overlays when you are done. Note, that all πdynamic variables residing on the heap, before a RESTOREHEAP call is made, areπlost. (But, you could code around this by manipulating the free list).ππRESTOREOVERLAY clears the heap again and restores the heap pointers to pointπabove the overlay buffer. You may then start using overlaid procedures andπfunctions again.ππThis is a simple example and may not be suitable for all purposes.πππππ 10 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND TOP TEN QA IMPORT 47 Top Ten Turbo Pascal Technical Support Questionsπππ 1. How do you read and write a file inside a Turbo Pascalπ program?ππ Answer: The following example demonstrates how to create, π write and read from a text file.ππ Program FileDemo;π Var FileVar : Text;π InString,OutString : String;π Beginπ OutString := 'Write this to a text file';π Assign(FileVar,'TEST.TXT'); π Rewrite(FileVar); {Creates a file for writing}π Writeln(FileVar,OutString); π Close(FileVar);π Assign(FileVar,'TEST.TXT'); π Reset(FileVar); {Opens an existing file for reading}π ReadLn(FileVar,InString);π Close(FileVar);π End. π ππ 2. Where is the GRAPH.TPU file?ππ Answer: The GRAPH.TPU is archived in the BGI.ARC file. Useπ the UNPACK.COM program to dearchive GRAPH.TPU from theπ BGI.ARC file. For example: π π UNPACK BGIππ 3. How do you send a program's output to the printer?ππ Answer: Use the LST file variable declared in the PRINTERπ unit. For example:ππ Program SendToPrinter;π Uses Printer;π Beginπ Writeln(LST,'This will go to the printer.');π End.ππ 4. Why am I getting a "Unit file format error" when I compileπ my program with the new Turbo Pascal compiler?ππ Answer: You are using a unit that has been compiled with aπ different Turbo Pascal version. From the main program, use π the BUILD option to recompile all dependent units.πππ 5. How do you dump graphics to a printer?ππ Answer: Please see the enclosed Technical Informationπ Handouts, #433 and #432, for examples of printing graphicsπ on Epson compatible and HP Laser Jet printers.πππ 6. How do you communicate with the serial port?ππ Answer: Modify the AuxInOut unit described in the Referenceπ Guide as follows: To read incoming data, use the Resetπ procedure before reading. Also, you should read from a π buffer rather than directly from the serial port. π Furthermore, the AuxInOut is not an ideal example to use asπ input buffering is not supported; you should use the π enclosed Technical Information Handout #407 routines forπ this purpose. πππ 7. Why doesn't the Exec procedure execute my subprograms?ππ Answer: Make certain that you use the {$M} directive to setπ the maximum heap size to the lowest possible value. If thisπ is done, check the value of the variable DosError toπ diagnose other problems. What is DosError Returning afterπ the call: π π 8) Not enough Memory: User needs to lower MAX Heap π {$M Stack, Min, Max} π 2) File not found: User needs to specify the fill Path andπ extension of the command. If you'reπ trying to execute a DOS internalπ command, you need to use COMMAND.COMπ (see DIR example in manual).πππ 8. What do I do about running out of memory during compilation?ππ Answer: There are a number of solutions to this problem:ππ 1. If Compiler/Destination is set to Memory, set it to Diskπ in the integrated environment.π 2. If Options/Compile/Link buffer in the integratedπ environment is set to Memory, set it to Disk. π Alternatively, if you're using 4.0, place a {$L-}π directive at the beginning of your program. Use the /Lπ option to link to disk in the command-line compiler.π 3. If you are using any memory-resident utilities, such asπ Sidekick and Superkey, remove them from memory.π 4. If you are using TURBO.EXE, try using TPC.EXE instead -π it takes up less memory.π 5. Turn off any compiler directives which are notπ necessarily needed. By simply turning off rangeπ checking {$R-} and software emulation {$E-}, your codeπ size will be reduced dramatically.π 6. Move all units, except PRINTER.TPU, out of the TURBO.TPLπ file and into the installed units directory.π π If none of these suggestions help, your program or unit may π simply be too large to compile in the amount of memoryπ available, and you may have to break in into two or moreπ smaller units. Alternatively, if you're using 5.0+, youπ should consider using overlays.πππ 9. How can my program be over-writing memory?ππ Answer: The most common causes for memory overwrites are:ππ 1. Indexes out of range (Turn range checking on {$R+}) π 2. Uninitialized variables (Write an initialization proc) π 3. Pointers out of bounds (Verify that pointers are not π pointing outside of the heap space) π 4. Improper use of FillChar or Move (Be sure to use theπ SizeOf function) π 5. Illogical operations on stringsπ ππ 10. How come I don't get the results that I expect when Iπ compare and print real numbers?ππ Answer: The problem with real numbers in Turbo Pascal is aπ problem with how a real number is stored in binary form. A π binary number has no decimal point and thus a real number π cannot directly translate into a binary number easily. π Calculations must be performed to break a real number downπ into it's binary representation. As with any calculation π that involves division or multiplication, small rounding π errors will occur. The problem you are experiencin g is aπ rounding error that occurs during translation from a realπ number into it's binary representation and back. I suggestπ that you round the results of your calculation to the numberπ of decimal points that you require to alleviate the problem.π 11 06-01-9306:59ALL SWAG SUPPORT TEAM BORLAND QA for TPW IMPORT 81 QNA-4/8/91πTurbo Pascal for Windowsπ1.0πLocal Heap and Global HeapπTPW's New, GetMem, Dispose and Freemem all use Window's global heap.ππ[[[]]]ππThe Local Heap is very small, and not used by TPW, and is occasionally usedπby Windows functions (like edit controls).ππThe Global Heap is generally very big, and is where your pascal program'sπ"heap" is stored. The GetMem, FreeMem, New, and Dispose procedures allπoperate on the Global Heap.ππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πExec from within a TPW program.πUse WinExec in the WinDOS to launch DOS and WinApp programs.ππ[[[]]]πQuestion:π I want to be able to EXEC a DOS or Windows program in Windows. How do Iπ do that?πAnswer:π Take a look at the WinExec procedure in your Windows Reference Guide.π You can use Exec on both WinApps and DOSApps.πππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πTPW New and GetMem proceduresπNew, GetMem, HeapBlock, HeapLimit in TPWππ[[[]]]πThe way memory allocation works for new and getmem is as follows:ππThere are two system variables: HeapBlock and HeapLimit.ππWhen memory is allocated for a size less than HeapBlock (Default is 1k)πthen the memory will be suballocated in a block of size HeapBlockπ(default is 8k).πAllocation of blocks larger than HeapLimit will have there own block. Allπallocations will be in global memory.ππYou will not be able to change the way this works.πππ<<<>>>πQNA-4/8/91πTurbo Pascal Windowsπ1.0πWinCrt and Windows 3.0πWinCrt cannot be used with OWL.ππ[[[]]]ππ WinCrt is an emulation of the Dos and Crt units. Basically, it provides aπquick and dirty means of output to a Windows window. It's not intendedπto be a Dos to Windows porting service - WinCrt apps are not full Windowsπapps - just a Windows window with some text, key scanning loops, andπscrollbars.ππ Note that writelns to the 'screen' will cause a runtime error in TPWπunless the WinCrt unit is used. (WinCrt opens the standard Input andπOutput files, which are closed by default in TPW). The Readln, Keypressed,πprocedures, etc., are also only valid under the WinCrt umbrella. The textπcolors and text inverse are not supported. And since WinCrt takes control ofπthe message loop and dispatch services of your windows app, WinCrt cannot beπused in conjunction with OWL. You can call Windows API functions directly,πhowever.πππ<<<>>>πQNA-4/8/91πTurbo Pascal Windowsπ1.0πPChars in TPW.πWhat is a PChar type?ππ[[[]]]ππ PChar is a special pointer type that has been added to the PascalπWindows language definition. A PChar is a pointer to an array ofπcharacters with a maximum size of 64K and terminated by a null characterπ(#0). This is a C string, and comes with all the memory management hasslesπand pointer arithmetic advantages of C. The PChars and arrays of char areπtype compatible, and a Strings unit is devoted to C-string manipulationπfunctions (all start with "STR"). When passing a string literal to aπprocedure, the compiler will figure out whether the string literalπshould be stored in c-string format or in pascal string format, or both.πππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πTDW popping in dual mode in TPW.πHow to make TDW come up in dual mode from within TPW.ππ[[[]]]πQuestion:π I want to make TDW come up in dual mode from within the TPW.πAnswer:π Add the following lines to the tpw.ini file:ππ [Debugger]π Exepath=<pathname>π Switches=<command-line options>ππ where <pathname> is the path to the TDW.EXE and <command-line options>π are the options you normally want to use with TDW, this will do it. Theπ command-line option for the dual display is -do.πππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πChanging Button TextπUse SetCaption method in OWL or SendMessage API to change button text.ππ[[[]]]πQuestion:πHow do you change the text on a button?ππAnswer:πTo change the text on a button, send a WM_SETTEXT message with the string forπthe lParam. For example:ππ SendMessage(btnWindow, WM_SETTEXT, 0, 'New Button Title');ππOr if you are using OWL, call the SetCaption method of TButton.πππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πStatus line in a MDI Application.πHow can I have a status line in an MDI Application?ππ[[[]]]ππTo have a status line in an MDI application, override the windows MDIπWMSize method like this:ππ TGDIDemoWindow.WMSize(var Message: TMessage);π beginπ TMDIWindow.WMSize(Message);π {Calculate the new size of the client area}π MoveWindow(ClientWnd^.HWindow, size of the new client area)π end;ππThis will keep the client window from obscuring your status line or otherπcontrols you might like to have in your window.πππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πTPW 1.0 Hot Sheet.πFeature set of TPW 1.0.ππ[[[]]]πTurbo Pascal for Windows 1.0 Hot Sheet.ππFeature Highlightsπ----------------------πNEW! state-of-the-art Windows integrated development environmentπ(IDE).ππ Runs under Windowsπ Multiple editor windowsπ Full Mouse Supportπ Supports TP6 type hot keysπ Multi-file editor that can edit files up to 1MBπ complete save and restore of desktopππNEW! ObjectWindow library - Built in support for Windows, Menus,πDialogs, Buttons, List boxes, Edit fields, Icons and more. Allπfor use in your applications.ππNEW! Full access to all Windows API functions and messages.ππNEW! Fully Supports creation of DLLs.ππNEW! Turbo Debugger for Windowsππ Supports Windows messagesπ Advanced breakpointsπ Reverse executionπ Automatic DLL debuggingπ Object browser and inspectorsπ Single and dual monitor supportππINCLUDED! Whitewater Resource Toolkit - Visually create Dialogs,πMenus, Icons, Bitmaps and String resources.ππINCLUDED! Resource CompilerππINCLUDED! Windows Help CompilerππFull featured inline assembler (BASM)ππPrivate fields and methods in object declarationsππExtended syntax directive ($X) that lets you treat functions likeπprocedures (and ignore function results)ππ286 code generationππAddress references in typed constantsππFar and near procedures directives ($F)ππLink in initialized data ($L) from object (OBJ) filesππSmart linker removes unused objects and codeππcomplete math coprocessor emulation and support -π8087/80287/80387ππNEW! Turbo Help hypertext on-line help facilities, includingπreferences to all Windows API finctions and messages.ππTurbo Pascal for Windows includes everything you need to createπWindows applications. It does not require the Microsoft WindowsπSDK.ππSystem Requirementsπ---------------------πIBM PC or PS/2 and all 100% compatiblesπMicrosoft Windows 3.0 or laterπ2Mb of memoryπEGA, Hercules or VGA graphicsπMouse or other pointing deviceπHard Disk (Requires 6.5 Meg for full product)ππBenchmarksπ-----------πMachine Lines/MinπCompaq DeskPro 386/33 84,000ππDocumentationπ----------------π1. User's Guideπ2. Programmer's Guideπ3. Windows Reference Guideπ4. Windows Programming Guideπ5. Whitwater Resource Toolkit Users Guideπ6. Turbo Debugger for Windows Users Guideπ7. Help Compiler Reference GuideππPricing Informationπ-------------------------πTurbo Pascal for Windows $249.95πππSpecial Offer Informationπ---------------------------πRegistered user of ANY BORLAND LANGUAGE PRODUCT $99.95 ***πππ***Turbo Pascal for Windows is exclusively for WindowsπDevelopment and does not replace Turbo Pascal version 6.0. TP6πis the current product for standard DOS development. The offerπis not an upgrade it is a limited special offer to our currentπTurbo Pascal Customers.ππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πThe $G+ directive, 286 code generation, and Real Mode.πHow can I detect if Windows is in Real mode?ππ[[[]]]πQuestion:ππIf I use the $G+ 286 code generation directive, how can I be sure thatπWindows is running in Real mode?ππAnswer:π The 286 code generation option allows the compiler to substitute moreπefficient 286 instructions in place of normal 8088 instructions for thingsπlike loops, jumps and memory operations. If you compile your program withπ$G+, then you should call GetWinFlags early in the startup of yourπapplication - such as in your Application.Init. Check for the wf_PtModeπflag set. If it's not set, then you're not running in protected mode, andπyou should display a message box and exit your program. Setting theπApplication's status variable to a non-zero value should also work, althoughπthat would cause more code to be executed, implying greater risk of hitting aπ286 instruction.ππ All the precompiled units in TPW will run in real mode and are notπaffected by the $G+ directive. The $G+ directive only affects code that youπcompile yourself.ππ<<<>>>πQNA-4/8/91πTurbo Pascal for Windowsπ1.0πUsing the 80X87 emulator in TPW.πDo I need a coprocessor to run TPW?ππ[[[]]]πQuestion:π Must TPW a the math coprocessor?ππAnswer:π Windows implements its own 80x87 instruction emulator as a DLL that isπloaded when an 80x87 instruction is detected. The reason that the '87 codeπgeneration is an option is speed: emulators are always slower than theπreal hardware, and slower than using the 6 byte reals. If you need double,πor extended reals, or Comp integers, then you must turn on '87πcode generation, and Windows will adjust itself to compensate if thereπis no '87 hardware when the program is running. You do not need aπcoprocessor to compile or run the program.ππ<<<>>>ππTN-4/8/91πTurbo Pascal for Windowsπ1.0πTPW "Can't find Debugger" error messageπ"Can't find Debugger" error message can be caused by not enough memoryπavailable.π[[[]]]πWith Turbo Pascal for Windows you can get the Can't Find Debugger errorπmessage when there is not enough memory to load the debugger. Thereπmust be at lease 250K free in order to load the debugger.ππIf you have checked that TDW is in the path etc., and this error is stillπdisplayed, check the memory free...πππ<<<>>>πTN-4/8/91πTurbo Pascal for Windowsπ1.0πTPW Strings literals in the Data SegmentπTPW String literals are stored in the Data Segment.ππ[[[]]]πUnlike the DOS version of Pascal, String Literals are stored in the dataπsegment along with all global variables etc. If customer is running outπof data segment space, suggest using pointers to strings instead ofπstring literals.πππ<<<>>>π 12 06-08-9308:29ALL CHRIS PRIEDE BASM Tutoral #1 IMPORT 131 ===========================================================================π BBS: The Beta ConnectionπDate: 06-03-93 (00:08) Number: 680πFrom: CHRIS PRIEDE Refer#: NONEπ To: ALL Recvd: NO πSubj: BASM TUT01 (1/4) Conf: (232) T_Pascal_Rπ---------------------------------------------------------------------------ππ No matter what HLL you use -- Pascal, C, COBOL or some otherπlanguage -- there is still place for assembly in your programs.πCorresponding directly to the native language of your computer, itπcombines unlimited control with unmatched efficiency.ππ Since Borland added the built-in assembler in TP 6.0, enhancingπPascal programs with some assembly has become much easier than before.πHowever, I have read many books and found the BASM sections in TurboπPascal books are inadequate and frequently contain errors. There areπvery good assembly books available, but they focus on writingπassembly-only programs using standalone assemblers.ππ Considering this, I decided to write a text file -- assemblyπlanguage lessons for TP programmers, mostly using BASM. When I asked ourπhost Guy for his opinion on this idea, he aproved and suggested I postπsections weekly in this conference.ππ A large part of this tutorial will be dedicated not to assemblyπlanguage itself, but tasks that often require it: writing interruptπhandlers and TSRs, accessing hardware directly. I will try to post newπsections weekly, if my schedule permits. Questions, suggestions andπcriticism are very welcome.ππ You will need a copy of TP 6.0 or later. Turbo Debugger, TASM orπMASM could be useful, but are not required.πππ - I -ππ To get started, we will take a Pascal routine, convert it toπassembly (using BASM) and see if we can beat the compiler at codeπgeneration. We will use a simple integer square root function; it is notπsomething you would need often (unless you are writing graphicsπroutines), but serves well as an example and only requires simple dataπmovement and arithmetic instructions.ππfunction ISqr(I: word): word;πvar Root, LRoot: word;πbeginπ Root := 1;π repeatπ LRoot := Root;π Root := ((I div Root) + Root) div 2;π until ((integer(Root - LRoot) <= 1) andπ (integer(Root - LRoot) >= -1));π ISqr := Root;πend;ππ It is based on a well-known formula (name has escaped my memory).πThe loop usually continues until Root and LRoot are equal, but thatπmight never happen with integers because fraction is truncated. Ourπversion loops until difference is less than 1, resulting in almostπcorrectly rounded result. The number of iterations required to findπsquare root of N never exceeds (ln N) +1, which means our function willπfind the square root of any valid argument in 12 or less iterations.ππ Now, let's convert this to assembly. One major improvement we canπmake is to place both temporary variables in registers. CPU can accessπregisters much faster than memory. AX and DX are needed for division, soπwe will assign Root to register CX and LRoot -- to BX:ππfunction ISqr(I: word): word; assembler;πasmπ mov cx, 1 { Root := 1 }π@@1: { loop start label }π mov bx, cx { LRoot := Root }π mov ax, I {< < }π sub dx, dx {< AX := (I div Root) < }π div cx {< < Root := ((I div Root) }π add ax, cx { AX := AX + Root < + Root) div 2 }π shr ax, 1 { AX := AX div 2 < }π mov cx, ax { Root := AX < }π sub ax, bx { AX := Root - LRoot }π cmp ax, 1 { Compare AX to 1... }π jg @@1 { Greater than 1 -- continue loop }π cmp ax, -1 { Compare AX to -1... }π jl @@1 { Less than -1 -- continue loop }π mov ax, cx { ISqr := Root -- return result in AX }πend;ππ Simple statements translate to one instruction, while complexπexpressions have to be broken down in smaller steps. Notice we computeπexpression (Root - LRoot) only once, although it's result is testedπtwice. As you will see shortly, Turbo Pascal is not smart enough yet toπtake advantage of this: compiled Pascal code would subtract twice.ππ(continued in next message...)π---π * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636π * PostLink(tm) v1.05 DWTOOLBOX (#1035) : RelayNet(tm)π===========================================================================π BBS: The Beta ConnectionπDate: 06-03-93 (00:10) Number: 681πFrom: CHRIS PRIEDE Refer#: NONEπ To: ALL Recvd: NO πSubj: BASM TUT01 (2/4) Conf: (232) T_Pascal_Rπ---------------------------------------------------------------------------π Function result is left in AX register. TP expects function returnπvalues in the following registers:ππ Char, Byte : ALπ Word, Integer : AXπ LongInt, pointers : DX:AXπ (low order word/offset in AX, high order word/segment in DX)πππ Let's go through this line by line...ππ* function ISqr(I: word): word; assembler;ππ This is a standard function declaration, except for the wordπ"assembler", which tells Turbo Pascal entire function is going to be inπassembly (if you try to insert some Pascal code in assembler function,πit won't work).ππ* asmππ "Asm" means start of assembly block, just like "begin" means startπof Pascal block. Since our function was declared assembler, it has onlyπasm block; without "assembler" keyword it would have to start withπ"begin":ππfunction Foo;πbeginπ asmπ { some assembly code }π end;πend;ππ You can use asm blocks anywhere in your Pascal code, but conventionsπfor pure asm functions are somewhat different.ππ* mov cx, 1ππ MOV (Move) instruction is assembly assignment statement. This lineπis equivalent to CX := 1 in Pascal.ππ* @@1:ππ This is a local label, not unlike Pascal labels, used with dreadedπGOTO statements. Unfortunately, the only means of flow control in asm isπusing GOTO-like jumps (conditional or unconditional), so you wouldπbetter get used to it. Destination of such jumps can be a Pascal-style,πpreviously declared label or a local label, like the one above. Localπlabels don't have to be previously declared, but they should start withπ@ (at sign).ππ* mov bx, cxπ* mov ax, Iππ Some more MOVing. Notice we can move data between two registers orπregister and memory (argument I is stored on stack -- in memory). Weπcan't, however, directly move one memory variable into another: mostπ80x86 instructions can't have two memory operands. If you ever need toπassign one memory variable to another, it should be done through aπregister, like this:ππ mov ax, Xπ mov Y, axππ The same applies to most other instructions with two operands.ππ* sub dx, dxππ SUB (Subtract) subtracts the right operand from left and leaves theπresult in left operand. SUB AX, CX is equivalent to AX := AX - CX inπPascal.ππ As you may have noticed, we are subtracting DX from itself. This isπa better way of setting register to 0 (100 - 100 = 0). We could use MOVπDX, 0, but SUB instruction is one byte smaller and a few clock cyclesπfaster. Some programmers use XOR for the same purpose: a number XORedπwith itself results in 0 too. We need DX to be 0 for division.ππ* div cxππ DIV (Divide). 80x86 divide and multiply instructions are differentπfrom other arithmetic instructions. You only need to specify divisor;πother operands are assumed to be AL, AH or AX, DX registers. This tableπsummarizes both DIV variants:ππDividend │Divisor │Quotient │Remainderπ───────────────┼───────────┼───────────────┼π16 bit (AX) │8 bit │8 bit (AL) │8 bit (AH)π32 bit (DX:AX) │16 bit │16 bit (AX) │16 bit (DX)ππ(continued in next message...)π---π * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636π * PostLink(tm) v1.05 DWTOOLBOX (#1035) : RelayNet(tm)π===========================================================================π BBS: The Beta ConnectionπDate: 06-03-93 (00:12) Number: 682πFrom: CHRIS PRIEDE Refer#: NONEπ To: ALL Recvd: NO πSubj: BASM TUT01 (3/4) Conf: (232) T_Pascal_Rπ---------------------------------------------------------------------------π The size of divisor selects between 16 and 32 bit divide. In ourπfunction both dividend and divisor are 16 bit words, which is why we hadπto zero DX, effectively extending word in AX to 32 bits.ππ Divisor should be a register or memory variable. If you need toπdivide by immediate value, first move it in a register:ππ mov bx, 320π div bxππ Use IDIV (Integer Divide) for signed numbers (integer, longint).πIDIV works exactly like DIV, but performs signed division.ππ* add ax, cxππ ADD (Addition). Pascal equivalent: AX := AX + CX.ππ* shr ax, 1ππ Bitwise Shift Right. Pascal equivalent: AX := AX shr 1. As the nameπimplies, this instruction shifts bits right:ππAX before shift: 0000101100110110 (decimal 2870)πAX after shift: 0000010110011011 (decimal 1435)ππ If you look at the decimal values on the right, you will noticeπshifting divided the number by 2. That is correct: shifting a binaryπnumber N bits left/right is equivalent to multiplying/dividing it byπ2^N. CPU can shift bits much faster than divide and shift instructionsπare not restricted to certain register(s) like DIV -- remember thisπwhen you need to multiply/divide by a power of 2.ππ The first operand (value to be shifted) can be either register orπmemory. The second operand is number of bits to shift -- immediate valueπor CL register. 8086 allows _only_ immediate value of 1; to shift by moreπthan one bit use the following:ππ mov cl, 3 (bit count)π shr ax, clππ You can also shift several times by one (I would use this method onlyπto shift by 2 - 3 bits, otherwise it gets too long):ππ shr ax, 1π shr ax, 1π shr ax, 1ππ 286+ can shift by any immediate count. If you are compiling for 286πand better ({$G+} compiler directive), you can do this:ππ shr ax, 3ππ* cmp ax, 1ππ CMP (Compare) compares two operands and sets CPU flags to reflectπtheir relationship. Flag state are later used to decide if a conditionalπjump instruction should jump or not. This two step process is used toπcontrol program flow, like if..then statements and loops in Pascal.ππ* jg @@1ππ ...and this is a conditional jump. JG (Jump if Greater) will transferπcontrol (GOTO) to label @@1 if the last compare found left operand to beπgreater than right, otherwise it "falls through": execution continues atπthe next instruction. JG assumes operands were signed (integers). Use JAπ(Jump if Above) for unsigned values (words). The following is a summaryπof conditional jumps for arithmetic relationships:ππJA/JNBE Jump if Above (">", unsigned)πJG/JNLE Jump if Greater (">", signed)πJAE/JNB Jump if Above or Equal (">=", unsigned)πJGE/JNL Jump if Greater or Equal (">=", signed)πJE/JZ Jump if Equal ("=")πJNE/JNZ Jump if Not Equal ("<>")πJB/JNAE Jump if Below ("<", usigned)πJL/JNGE Jump if Less ("<", signed)πJBE/JNA Jump if Below or Equal ("<=", unsigned)πJLE/JNG Jump if Less or Equal ("<=", signed)ππ For ease of use, assemblers recognize two different mnemonics forπmost conditional jumps. Use the one you find less cryptic.ππ Since conditional jump instructions simply inspect flags set byπprevious compare, there may be other instructions in between, providedπthey don't alter flags -- for example, MOV. Flags are not cleared andπcan be tested more than once. For example, you could do this:ππ(continued in next message...)π---π * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636π * PostLink(tm) v1.05 DWTOOLBOX (#1035) : RelayNet(tm)π===========================================================================π BBS: The Beta ConnectionπDate: 06-03-93 (00:29) Number: 683πFrom: CHRIS PRIEDE Refer#: NONEπ To: ALL Recvd: NO πSubj: BASM TUT01 (4/4) Conf: (232) T_Pascal_Rπ---------------------------------------------------------------------------π mov ax, Xπ cmp ax, Yπ jg @XGreater { X > Y }π jl @XLess { X < Y }π .... { fell through -- X = Y }ππ* end;ππ Like Pascal blocks, this means the end of BASM block. If you wereπusing a standalone assembler, you would have to add RET (Return fromπsubroutine) instruction and possibly some other (cleanup) code. BASMπadds this and similar purpose code at the top of the functionπautomatically -- it is called entry & exit code and usually amountsπto 1 - 3 instructions, depending from number of arguments and localπvariables.ππ Well, looks like everything is covered... Quite a bit for the firstπlesson too. If you have the instruction set reference, check it for moreπdetailed descriptions. If you don't, I strongly recommend to obtain one.πThis is just one of many sources:ππ The Waite Group's "Microsoft Macro Assembler Bible" or "TurboπAssembler Bible", ISBN 0-672-22659-6 (MASM flavor). $29.95, publishedπby SAMS. A very good reference book, comes in MASM and TASM flavors,πyou choose.ππ Finally, here is disassembly of compiled TP code. [BP+n] meansπreference to function argument, [BP-n] -- to local variable. This isπprovided mainly to satisfy your curiosity, but it shows we thought veryπmuch like the compiler, only coded it better:ππISQR:πisqr1.pas#9:beginπ 0000:0000 55 PUSH BPπ 0000:0001 89E5 MOV BP,SPπ 0000:0003 83EC06 SUB SP,+06πisqr1.pas#10: Root := 1;π 0000:0006 C746FC0100 MOV [WORD BP-04],0001πisqr1.pas#11: repeatπisqr1.pas#12: LRoot := Root;π 0000:000B 8B46FC MOV AX,[BP-04]π 0000:000E 8946FA MOV [BP-06],AXπisqr1.pas#13: Root := ((I div Root) + Root) div 2;π 0000:0011 8B4604 MOV AX,[BP+04]π 0000:0014 31D2 XOR DX,DXπ 0000:0016 F776FC DIV [BP-04]π 0000:0019 0346FC ADD AX,[BP-04]π 0000:001C D1E8 SHR AX,1π 0000:001E 8946FC MOV [BP-04],AXπisqr1.pas#14: until ((integer(Root - LRoot) <= 1) andπisqr1.pas#15: (integer(Root - LRoot) >= -1));π 0000:0021 8B46FC MOV AX,[BP-04]π 0000:0024 2B46FA SUB AX,[BP-06]π 0000:0027 3D0100 CMP AX,0001π 0000:002A 7FDF JG isqr1.pas#12(000B)π 0000:002C 8B46FC MOV AX,[BP-04]π 0000:002F 2B46FA SUB AX,[BP-06]π 0000:0032 3DFFFF CMP AX,0FFFF (-1)π 0000:0035 7CD4 JL isqr1.pas#12(000B)πisqr1.pas#16: ISqr := Root;π 0000:0037 8B46FC MOV AX,[BP-04]π 0000:003A 8946FE MOV [BP-02],AXπisqr1.pas#17:end;π 0000:003D 8B46FE MOV AX,[BP-02]π 0000:0040 89EC MOV SP,BPπ 0000:0042 5D POP BPπ 0000:0043 C20200 RETN 0002ππ TP's code doesn't place variables in registers and does someπunnecessary work (ie, subtracts Root - Lroot twice; see above). Entryπand exit code is shown too.ππ Our version is slightly faster, but I didn't pick this routine toπdemonstrate optimization -- integer math is about the only area whereπmost compilers do well. A good optimizing compiler should be able toπgenerate code as good as ours. I would be curious to see what thisπlooks like compiled with SBP+ (Guy: consider this a strong hint :)).πMost code you would normally write in assembly will show much greaterπimprovement (string routines, etc.).ππ What to expect: next time we will discuss how to call DOS or BIOSπinterrupts using BASM; what registers are available; which have specialπuses or should be preserved; how to access strings, arrays and records.πSomewhere in near future: writing object methods in BASM.ππ * * *π---π * D.W.'s TOOLBOX, Atlanta GA, 404-471-6636π * PostLink(tm) v1.05 DWTOOLBOX (#1035) : RelayNet(tm)π 13 08-17-9308:46ALL JACK MOFFITT Modem Reference IMPORT 163 ~O From: JACK MOFFITT Refer#: NONEπ To: ALL Recvd: NOπSubj: MODEM REFERENCE 1/ Conf: (1221) F-PASCALπ---------------------------------------------------------------------------π Pascal Programmer's Reference to Modem Communicationsππ byππ Jack Moffittππ___-------------------------------------------------------------------------πππINTRODUCTIONπ~~~~~~~~~~~~π Direct UART programming is a subject that not many people areπfamiliar with. Since the advent of FOSSIL, many people advise that oneπshould use that for all communications, to make it more portable. But forπsome instances, it is necessary to have internal modem routines to go on.πBecause no one seems to know or understand this subject, and because I haveπfound no other texts on the subject, I have decided to put it all into oneπtext, and maybe round off the edges on this subject.πππTHE ASYNCRONOUS MODEMπ~~~~~~~~~~~~~~~~~~~~~π The asyncronous modem uses one (or more) specific ports on aπcomputer, as well as an IRQ (Interrupt Request). Every time a characterπof data is received in the device, an interrupt is processed. One mustπmake a interrupt service routine to handle this input, but where does it go?πSince the IRQs are tied into interrupts, knowing the IRQ the device is using,πwe can replace that interrupt. The port addresses and IRQ vectors are asπfollows:ππPort Addresses: COM1 -- 03F8h IRQ Vectors : 0 -- 08hπ COM2 -- 02F8h 1 -- 09hπ COM3 -- 03E8h 2 -- 0Ahπ COM4 -- 02E8h 3 -- 0Bhπ 4 -- 0Chπ 5 -- 0DhπStandard Port IRQs: COM1 -- 4 6 -- 0Ehπ COM2 -- 3 7 -- 0Fhπ COM3 -- 4 8 -- 70hπ COM4 -- 3 9 -- 71hπ 10 -- 72hπ 11 -- 73hπ 12 -- 74hπ 13 -- 75hπ 14 -- 76hπ 15 -- 77hππFor standard use, the IRQ for comm ports 1 and 3 is 4, and for 2 and 4 it'sπ3. The 8250 UART has 10 registers available for getting, receiving andπinterperating data. They are all located at offsets from the base addressπof the port. Here are the registers and their offsets:ππRegister Offsets: Transmitter Holding Register (THR) -- 00hπ Receiver Data Register (RDR) -- 00hπ Baud Rate Divisor Low Byte (BRDL) -- 00hπ Baud Rate Divisor High Byte (BRDH) -- 01hπ Interrupt Enable Register (IER) -- 01hπ Interrupt Identification Register (IIR) -- 02hπ Line Control Register (LCR) -- 03hπ Modem Control Register (MCR) -- 04hπ Line Status Register (LSR) -- 05hπ Modem Status Register (MSR) -- 06hππWith this information one can address any register by adding the offset toπthe base address. Therefor, if one is using COM2 (base address 02F8h) theyπwould access the Modem Status Register with: port[$02F8 + $06].πππTRANSMITTER HOLDING REGISTERπ~~~~~~~~~~~~~~~~~~~~~~~~~~~~π This register contains the data to be sent to the remote PC or modem.πWhen bit 5 (THR empty) of the LSR is set, one can write to this port, thusπsending data over the phone line or null modem cable.πππRECEIVER DATA REGISTERπ~~~~~~~~~~~~~~~~~~~~~~π This register contains the incoming data. Read this register onlyπif bit 0 (Data Received) of the LSR is set, otherwise one will getπunpredictable characters.πππBAUD RATE DIVISORπ~~~~~~~~~~~~~~~~~π The Baud Rate Divisor is used to set the BPS rate. To calculate theπBaud Rate Divisor, one must use the formula: (UART Clock Speed)/(16*BPS).πThe UART Clock Speed is 1843200. To set the BRD one must first set bit 7π(port toggle) of the Line Control Register to 1, and then write the low andπhigh bytes to the correct offsets. Always remember to reset LCR bit 7 to 0πafter one is finished setting the BPS rate.πππINTERRUPT ENABLE REGISTERπ~~~~~~~~~~~~~~~~~~~~~~~~~π The IER is used to simulate real interrupt calls. Write a byteπcontaining to interrupt information to enable any interrupts, all interruptsπalso have corresponding actions to clear the interrupts. Here's the list:ππInfo Byte:ππbit 7-6-5-4 3 2 1 0π ~~~~~~~ ~ ~ ~ ~π Always 0 MSR Change Data Error or Break THR empty Data ReceivedππTo Clear: Read MSR Read LSR Output to THR Read RDRπππINTERRUPT IDENTIFICATION REGISTERπ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~π This register is used to determine what kind of interrupts haveπoccured. Read one byte from this register, and use AND masks to find outπwhat has happened. The information in the byte is:ππInfo Byte:ππbit 7-6-5-4-3 2-1 0π ~~~~~~~~~ ~~~ ~π Unused 0-0 = Change in MSR If this bit is setπ 0-1 = THR empty more than oneπ 1-0 = Data Received interrupt hasπ 1-1 = Data Error or Break occured.πππLINE CONTROL REGISTERπ~~~~~~~~~~~~~~~~~~~~~π The Line Control Register (LCR) is used for changing the settingsπon the serial line. It is also used for initializing the modem settings.πWrite a byte to the port, containing the following info:ππLCR Byte.ππ Port Toggle Break Condition Parity Stop Bits Data Bitsπbit 7 6 5-4-3 2 1-0π ~ ~ ~~~~~ ~ ~~~π 0 = Normal 0 = Off 0-0-0 = None 0 = 1 0-0 = 5π 1 = Set BRD 1 = On 1-0-0 = Odd 1 = 2 0-1 = 6π 1-1-0 = Even 1-0 = 7π 1-0-1 = Mark 1-1 = 8π 1-1-1 = SpaceπEverything is pretty clear except for the purpose of bits 6 and 7. Bit 6πcontrols the sending of the break signal. Bit 7 should always be 0, exceptπif one is changing the baud rate. Then one must set it to one, write toπthe BRD and then set it back to zero. One can only write to the BRD if thisπbit is set.πππMODEM CONTROL REGISTERπ~~~~~~~~~~~~~~~~~~~~~~π The MCR is used to control the modem and it's function. Write oneπbyte to the MCR containing the following info:ππMCR Byte.ππbit 0 = Set DTR Lineπ 1 = Set RTS Lineπ 2 = User Output #1π 3 = User Output #2π 4 = UART Loopbackπ 7-6-5 = Unused (Set to 0)ππTypically one will set bits 3 through 0 to 1. Bit 4 is used for testingπtheir routines without another modem, and the other bits are unused, butπshould always be set to 0.πππLINE STATUS REGISTERπ~~~~~~~~~~~~~~~~~~~~π The LSR reports the current status of the RS232 serial line. Theπinformation contained is obtained by reading one byte from the LSR. Theπbits and the info associated with each are listed below.ππLSR Byte.ππbit 0 = Data Receivedπ 1 = Overrun Errorπ 2 = Parity Errorπ 3 = Framing Errorπ 4 = Break Detectπ 5 = THR emptyπ 6 = Transmit Shift Register (TSR) emptyπ 7 = Time OutππThe TSR takes the byte in the THR and transmits is one bit at a time. Whenπbit 0 is set one should read from the RDR, and when bit 5 is set one shouldπwrite to the THR. What actions are taken on various errors are left up toπthe programmer.πππMODEM STATUS REGISTERπ~~~~~~~~~~~~~~~~~~~~~π Just like the LSR returns the status of the RS232 line, the MSRπreturns the status of the modem. As with other registers, each bit in theπbyte one reads from this port contains a certain piece of info.ππMSR byte.ππbit 0 = Change in CTSπ 1 = Change in DSRπ 2 = Change in RIπ 3 = Change in DCDπ 4 = CTS onπ 5 = DSR onπ 6 = RI onπ 7 = DCD onππCarrier Detect is achieved by testing bit 7, to see if the line is ringingπtest bit 6.ππππPUTTING IT ALL TOGETHERπ~~~~~~~~~~~~~~~~~~~~~~~π One can now use this information about the 8250 UART to startπprogramming their own modem routines. But before they can do that, theyπmust learn a little about interrupts and the 8259 PIC (ProgrammableπInterrupt Controller). This information is necessary to write modemπroutines that are not dependant on a slow BIOS.πππINTERRUPTSπ~~~~~~~~~~π Interrupts are a broad subject, and this is not a reference for them.πFor for information on interrupts, one should look at DOS Programmer'sπReference 4th Edition. Although there are two kinds of interrupts - Non-πMaskable and Maskable, maskable interrupts are the only ones that one shouldπbe concerned with. When an interrupt generates, the processor finishes theπcurrent command, and then saves a few variables (the address to return to)πon the stack and jumps to the vector of the interrupt. One can turn offπmaskable interrupts with the STI, and back on with CLI. One can not turnπoff non-maskable interrupts. Replacing interrupt routines in pascal is veryπeasy. Include the DOS unit in their program, and use the procedures GetIntVecπand SetIntVec. To replace the interrupt for COM2 (remember it's 0Bh) oneπwould do this:π GetIntVec($0B, OldInt0Bh);π SetIntVec($0B, NewInt0Bh);πAt the end of the program, one MUST restore the interrupt using:π SetIntVec($0B, OldInt0Bh);πFailing to do this will most likely result in a system crash after theπprogram terminates. Because another interrupt may be called inside anotherπinterrupt at any time, it is necessary to turn off interrupts, as mentionedπabove, every once in a while. Remember all this, and programming for theπmodem will be much easier ( :) ).πππ8259 PROGRAMMABLE INTERRUPT CONTROLLERπ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~π The 8259 PIC is used by the processor as a gateway for interrupts.πThe 8259 decides which interrupts go first and which are currently active.πThe order interrupts are processed in in the order of their IRQ number.πThus, IRQ0 will always be processed before IRQ1 if both are generated at theπsame time. Since asyncronous communication uses IRQs, we must instruct theπ8259 PIC on when are interrupts should start interrupting, and when theyπshould stop. When initializing the modem, one must "turn on" the IRQ beforeπone can start to use it. Turning back off is identical, but don't turn itπoff if one is writing door routines! To do either requires one assign theπvalue contained at the port the value AND the mask. The masks for turningπon and off the 8259 follows.ππTo Turn On:π mask = (1 shl (IRQ number)) xor $00FFπTo Turn Off:π mask = 1 shl (IRQ number)ππOne must also reset the PIC in the custom interrupt handler after one isπfinished with it. That will allow the PIC to process the next interrupt.πTo reset the PIC, write 20h to it. This is also refered to as the End OfπInterrupt (EOI) signal. This must also be done after first initializing theπmodem. There is another PIC on the 286, allowing the last 8 IRQs (7 - 15).πThe second PIC is called the cascade PIC. The addresses for the PIC commandπand mask ports are listed next.ππ8259 PIC command address = 20hπ8259 PIC mask address = 21hπCascade 8259 PIC command address = A0hπCascade 8259 PIC mask address = A1hππTo reset the PIC always write to the command, and for turning off with theπmasks always write to the mask. The masks for the cascade PIC are the sameπfor the other PIC. So the mask for IRQ0 is equal to the mask for IRQ7.πAlso, one should write 20h to the cascade PIC as the EOI signal.πππINPUT/OUTPUT CONTROLπ~~~~~~~~~~~~~~~~~~~~π To keep the text simple, only buffered input will be covered.πBuffered output is a subject of more depth than one can provide in a shortπreference. Buffered input is relatively simple, but there are a few thingsπone must consider. The size of the buffer is very import, make the bufferπto big and one will eat up the datasegment, make the buffer to small andπone will get overruns. A good choice for a general buffer would be in theπrange of 4 to 8k. This should allow plenty of room for all incoming data.πAnother inportant factor is the type of buffer. For simplicity and ease ofπuse, a circular input buffer is recommended. A head and a tail pointπto the start and end of the buffer, and they will both wrap around whenπeither go past the end of the buffer, thus making the buffer a kind ofπcircle. Getting data in the buffer is the primary job of the customπinterrupt routine. Clearing the buffer and reading characters from theπbuffer is then as easy as reading a character from an array, and advancingπthe head of the buffer. Sending characters over the phone can beπaccomplished by waiting for the flow control and then sending the characterπto the THR, repeating for every character.ππTHE INTERRUPT SERVICE ROUTINEπ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~π The ISR (Interrupt Service Routine) is the backbone for asyncronousπcommunication. The interrupt is called for every character that comesπthrough the modem. So in the interrupt one must process these incomingπcharacters or else they will be lost. Since the the interrupt got called,πone must check the IIR (Interrupt Identification Register) to see whatπactually cause the interrupt to be called. Since the interrupt is mainlyπdealing with handling the incoming data, and for reasons of simplicity,πflow control will be ommited from the routine but will be discussed laterπin this text. Since one is writing to the buffer, and since anotherπcharacter is likely to come in during this time, one must disable interruptsπfor the shortest time possible while writing to the buffer, and then reenableπthem so no data is lost. (NOTE: If the ISR is to be contained in a unit, itπmust be declared in the unit's interface section as an INTERRUPT procedure.)πAfter disabling interrupts, checking for data, discarding data if no bufferπspace is available, putting the data in the buffer if there is room, andπclearing the RDR if any data error or break occured, one must turn on theπinterrupts and issue the EOI signal to the 8259 PIC or both the 8259 PICπand the cascade PIC if IRQ7 - IRQ15 is used. Here is a sample routine:πππconstπ BaseAddr: array[1 .. 4] of word = ($03F8, $02F8, $03E8, $02E8);π { Nice array to make finding the base address easy }ππvarπ Buffer: array[1 .. 4096] of char; { A 4k buffer for input }π Temp, { Varible to hold various modem statuses }π CommPort: byte; { Comm Port in use }π Head, { Start of the buffer }π Tail, { End of the buffer }π Size: word; { Size of the buffer }π Cascade: boolean; { For IRQ7 - IRQ15 }ππprocedure Async_ISR; interrupt; { NOTE: must declare the procedure interrupt }πbeginπ inline($FB); { STI - Disable interrupts }π Temp := port[BaseAddr[CommPort] + $02]; { Read a byte from the IIR }π if Temp and $06 = $04 then { Character received }π beginπ if Head <> Tail then { Make sure there is room in the buffer }π beginπ Buffer[Tail] := Chr(port[BaseAddr[CommPort] + $00]); { Read char }π inc(Tail); { Position the Tail for the next char }π if Tail > 4096 then Tail := 0; { If Tail is greater, wrap the buffer }π endπ else temp := port[BaseAddr[CommPort] + $00]; { Throw away overruns }π endπ else if Temp and $06 = $06 then { Data error or break }π Temp := port[BaseAddr[CommPort] + $00]; { Clear RDR }π inline($FA); { CLI - Enable interrupts }π port[$20] := $20; { Reset the 8259 PIC }π if Cascade then port[$A0] := $20; { Reset the cascade PIC }πend;πππFirst the procedure disables interrupts, then it reads the IIR to find outπwhat kind of interrupt needs processing. The procedure then masks out bitsπ2 and 1 and tests it to see if bit 4 is set. If data is received it checksπto make sure there is room in the buffer, and places the character at theπposition marked by Tail, otherwise it disregards the character as overrun.πIf a data error occured it clears the RDR to make sure no garbage isπreceived. Finally it enables interrupts and resets the 8259 (and the cascadeπif necessary).πππSENDING CHARACTERSπ~~~~~~~~~~~~~~~~~~π Sending character over the modem is much simpler than getting them.πFirst one must wait for the flow control and for the UART and then write theπcharacter to the THR. Here's an example:ππprocedure XmitChar(C: char); { Uses variable and constant declarations fromπbegin the previous example }π while ((port[BaseAddr[CommPort] + $05] and $20 <> $20) and { Wait for THR }π (port[BaseAddr[CommPort] + $06] and $10 <> $10)) { Wait for CTS }π do ; { Do nothing until CTS and THR empty }π port[BaseAddr[CommPort] + $00] := Ord(C); { Send character }πend;ππThis waits for the CTS signal and for the THR to be clear and then sends theπcharacter. To send strings just use this in a repeat loop such as:ππfor x := 1 to length(s) doπ XmitChar(s[x]);πππREADING CHARACTERSπ~~~~~~~~~~~~~~~~~~π The actual reading of character takes place in the ISR, but one stillπhas to get them from the buffer. Just read the character at the head ofπthe buffer and pass it back. An example:ππfunction RemoteReadKey: char; { Uses var and const from above }πbeginπ RemoteReadKey := Buffer[Head]; { Get the character }π inc(Head); { Move Head to the next character }π if Head > 4096 then Head := 0; { Wrap Head around if necessary }π dec(Size); { Remove the character }πend;ππTo find out if a character is waiting is even easier:ππfunction RemoteKeyPressed: boolean; { Uses vars and consts from above }πbeginπ RemoteKeyPressed := Size > 0; { A key was pressed if there is data inπend; the buffer }πππINITIALIZING MODEM PARAMETERS AND OTHER TOPICSπ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~π For most cases one can use interrupt 14h function 00h to initializeπmodem parameters, but if the baud rate is over 9600, this function willπnot work. One must change the BRD themselves. It is a simple matter ofπaccessing the BRD by setting the LCR bit 7 to 1 and writing to the BRD andπthen reseting the LCR bit 7 back to 0. Everything else, clearing buffers,πflushing buffers, formatting input, is all up to the programmer. I haveπprovided one with enough information to grasp the basis of modem programmingπand the I/O involved.ππFLOW CONTROLπ~~~~~~~~~~~~π Flow control is mainly used to prevent overflow error on today'sπhigh speed modems. CTS/RTS was already covered earlier, but nothing hasπbeen said for XOn/XOff. XOn/XOff will send a certain character (usuallyπa ^S) when the input buffer has reached a certain percentage of capacity.πThis signal is XOff. When the buffer has gone down to another percentage ofπcapacity, XOn (usually a ^Q) will be sent. It is the programmer's job toπlook for XOn/XOff codes and interperate them, as there are no standard waysπto do it as with CTS/RTS. It is also his job to make sure he or she sendsπthe signals at the appropriate time.πππCONCLUSIONπ~~~~~~~~~~π This text is general, and won't satisfy the needs of advanced modemπprogrammers. It was written to help those just starting, or thinking aboutπstarting, through the ordeal of finding a book, or read through source notπknowing what some of it does. If one finds any mistakes, please feel freeπto contact me via the Pascal FIDONet echo, and he will gladly correctπthem. Also, if one would like more information on other related topics,πcontact me via the Pascal echo, and I will try to help.ππ_____________________________________________________----πππI hope everyone will find this text useful, and please feel free toπcomment or correct anything. I posted it once but it got choped off inπplaces, so i'm posting it again. Enjoy.ππ Jackππ 14 08-27-9321:36ALL SWAG SUPPORT TEAM How Much Memory IMPORT 19 ~O > Can anyone help me in determining how much stack, and heap memoryπ> you need to give a TSR, does it depend on how many variables or howπ> long your code is?ππYour three requirements are based on the following:ππStack Spaceπ-----------πThis is based on how deep your procedure nesting goes, whether yourπprocedures are recursive and how much they'll recurse, how large theπParameters to those procedures are, and size of local variables. Keep inπmind that you only have to cover the largest/deepest combo of everythingπto be safe, not the total. TO be safe you should leave a little extraπfor interrupts and stuff to use ($100 or so)πI use $2000 for most programs, and $400 for my TSR's, which will hardlyπever use that much. $400 is the minimum you can declare.πIf you do alot of recursion or put huge things on the stack, use more.πActually, use the smallest number you can get away with without an errorπwhile stack checking is enabled.ππMinimum Heapπ------------πThis is the LEAST heap space your program can run with. Your programπABSOLUTELY HAS to have at least this much heap space when it's run.ππYou use heap space when you declare variables with New or getMem (likeπwith Turbo Vision or other objects)ππSo if you don't use any dynamic variables, you can pretty safely setπthis to 0. Otherwise, set it to a reasonable number for yourπapplication. (I mean if your database program only has memory for ONEπrecord, what use is it for it to run?)ππMaximum Heapπ------------πThis is the most heap space your program WILL reserve, even if more isπavailable. This needs to be large enough to hold all the dynamicπvariables you plan on allocating, plus a little...ππProblem with reserving it ALL is that you can no longer spawn childπprocesses with exec. So this needs to be small enough to let otherπprocesses you plan on running (dos shells) run, yet large enough so youπdon't run out of memory easily...ππThis is the toughest one to set.ππIn programs that don't use the heap, set it to 0.ππin programs that will never ever call a child process with Exec, set itπto 655360 ($100000, or all available memory)ππotherwise, your guess is as good as mine...ππI have my default set at 65536. ($10000)ππ 15 11-02-9306:06ALL ALTON PRILLAMAN MIDI Information SWAG9311 73 ~O {πALTON PRILLAMANππHOWEVER, <g> now would be a good time to learn about "Bitwise Operators"πto accomplish your goal With minimal memory requirements. I'll start Withπthe basics (no offense intended). You may have heard, or remember from aπProgramming class that a Byte is made up of 8 bits. When looking at a Byteπin binary, each bit holds a value of 0 or 1 that when put together inπtheir respective places will add up to make the number. Here's anπexample of a Byte:ππ B I N A R Yπ T A B L Eπ=========================================================================ππ Power | 7 6 5 4 3 2 1 0 | of 2π ------+---------------------------------+-----π Bit # | 8 7 6 5 4 3 2 1 |π ------+---------------------------------+-----π Value | 128 64 32 16 8 4 2 1 | HEXπ ------+---------------------------------+-----π 0 | 0 0 0 0 0 0 0 0 | $00π 1 | 0 0 0 0 0 0 0 1 | $01π * 2 | 0 0 0 0 0 0 1 0 | $02π 3 | 0 0 0 0 0 0 1 1 | $03π * 4 | 0 0 0 0 0 1 0 0 | $04π 5 | 0 0 0 0 0 1 0 1 | $05π 6 | 0 0 0 0 0 1 1 0 | $06π 7 | 0 0 0 0 0 1 1 1 | $07π * 8 | 0 0 0 0 1 0 0 0 | $08π 9 | 0 0 0 0 1 0 0 1 | $09π 10 | 0 0 0 0 1 0 1 0 | $0Aπ 11 | 0 0 0 0 1 0 1 1 | $0Bπ 12 | 0 0 0 0 1 1 0 0 | $0Cπ 13 | 0 0 0 0 1 1 0 1 | $0Dπ 14 | 0 0 0 0 1 1 1 0 | $0Eπ 15 | 0 0 0 0 1 1 1 1 | $0Fπ * 16 | 0 0 0 1 0 0 0 0 | $10π | |π * 32 | 0 0 1 0 0 0 0 0 | $20π * 64 | 0 1 0 0 0 0 0 0 | $40π * 128 | 1 0 0 0 0 0 0 0 | $80π | |π 255 | 1 1 1 1 1 1 1 1 | $FFπ ------+---------------------------------+-----ππ * = All columns to the right had filled up With 1s, so we carried to theπ next column to the left.ππNotice that when all of the "bit places" have a "1" in them, that theπtotal adds up to be 255 which is the maximum number that a Byte can hold.πIn binary (the inner part of the Chart), "1" is the maximum value a bitπcan hold Until it carries to the next column to the left. This brings usπto the next Chart, HEXIDECIMAL:πππ H E X I D E C I M A Lπ T A B L Eπ=========================================================================ππ Power| 1 0 |of 16 Power| 1 0 |of 16π -------+---------+----- -------+---------+-----π Decimal| | Decimal| |π Value| 16 0 | HEX Value| 16 0 | HEXπ -------+---------+----- -------+---------+-----π 0| 0 0 | $00 31| 1 1 | $1Fπ 1| 0 1 | $01 * 32| 2 0 | $20π 2| 0 2 | $02 33| 2 1 | $21π 3| 0 3 | $03 | |π 4| 0 4 | $04 47| 2 F | $2Fπ 5| 0 5 | $05 * 48| 3 0 | $30π 6| 0 6 | $06 63| 3 F | $3Fπ 7| 0 7 | $07 * 64| 4 0 | $40π 8| 0 8 | $08 79| 4 F | $4Fπ 9| 0 9 | $09 80| 5 0 | $50π 10| 0 A | $0A 95| 5 F | $5Fπ 11| 0 B | $0B * 96| 6 0 | $60π 12| 0 C | $0C 111| 6 F | $6Fπ 13| 0 D | $0D * 112| 7 0 | $70π 14| 0 E | $0E 127| 7 F | $7Fπ 15| 0 F | $0F * 128| 8 0 | $80π * 16| 1 0 | $10 255| F F | $FFπ 17| 1 1 | $11 * 256| |$0100π -------+---------+----- -------+---------+-----ππ * = All columns to the right had filled up With 15 (F) so we carriedπ to the next column to the left.ππThe hexidecimal table is derived from BASE 16. The value that each columnπmay hold a value of 15 (F) before we carry to the next column. Alsoπnotice that when both columns fill up With a value of "F" ($FF) that theπresult is 255, which is the maximum For a Byte.πππOkay, With that behind us, let's take a look at your application. As youπmay have noticed in the binary table in the previous message, a Byte willπgive us the ability to track up to 8 bits. Our goal here is to turn on orπoff each of the 8 bits as each channel is turned on or off. I assume thatπyou've got 16 channels to work With, so we'll use a Word instead of aπByte. When looked at in binary, a Word is like placing two Bytesπside-by-side. Notice that the HEXIDECIMAL works the same way.ππ 256-------------------------+ +---------------------------- 128π 512----------------------+ | | +------------------------- 64π 1024-------------------+ | | | | +---------------------- 32π 2048----------------+ | | | | | | +------------------- 16π 4096-------------+ | | | | | | | | +---------------- 8π 8192----------+ | | | | | | | | | | +------------- 4π 16384-------+ | | | | | | | | | | | | +---------- 2π 32768----+ | | | | | | | | | | | | | | +------- 1π | | | | | | | | | | | | | | | |π | | | | | | | | | | | | | | | |π Power | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0|of 2π-------+------------------------------------------------+-------π Bit # | 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1|π-------+------------------------------------------------+-------πDecimal| |π Value| BINARY | HEXπ-------+------------------------------------------------+-------π 1| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1| $0001π 2| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0| $0002π 4| 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0| $0004π 8| 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0| $0008π 16| 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0| $0010π 32| 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0| $0020π 64| 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0| $0040π 128| 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0| $0080π 256| 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0| $0100π 512| 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0| $0200π 1024| 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0| $0400π 2048| 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0| $0800π 4096| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0| $1000π 8192| 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0| $2000π 16384| 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0| $4000π 32768| 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| $8000π-------+------------------------------------------------+-------ππThough it has taken us a While to get here, you now have a "value" forπeach of your midi channels. if you need to use more than 16 channels, youπcan use the same methods applied above using a LongInt to give you a totalπof 32 channels (or bits).ππYou can now declare these as Constants in your Program like so:π}ππProgram MidiStuff;πConstπ {Midi Channels}π Ch1 = $0001;π Ch2 = $0002;π Ch3 = $0004;π Ch4 = $0008;π Ch5 = $0010;π Ch6 = $0020;π Ch7 = $0040;π Ch8 = $0080;π Ch9 = $0100;π Ch10 = $0200;π Ch11 = $0400;π Ch12 = $0800;π Ch13 = $1000;π Ch14 = $2000;π Ch15 = $4000;π Ch16 = $8000;ππVarπ MidiChannels : Word;ππ{ Now you can turn on or off each channel and check to see if one is set byπusing the following Procedures and Functions. You can accomplish this byπusing the or and and operators. }ππFunction ChannelIsOn(Ch : Word) : Boolean;πbeginπ ChannelIsOn := (MidiChannels and Ch = Ch);πend;ππProcedure TurnOnChannel(Ch : Word);πbeginπ MidiChannels := MidiChannels or Ch;πend;ππProcedure TurnOffChannel(Ch : Word);πbeginπ MidiChannels := MidiChannels and not Ch;πend;ππbeginπ MidiChannels := $0000; {Initialize MidiChannels - No channels on!}π TurnOnChannel(Ch2);π if ChannelIsOn(Ch2) thenπ Writeln('Channel 2 is on!')π elseπ Writeln('Channel 2 is off!');π if ChannelIsOn(Ch3) thenπ Writeln('Channel 3 is on!')π elseπ Writeln('Channel 3 is off!');π TurnOnChannel(Ch16);π TurnOnChannel(Ch10);π TurnOffChannel(Ch2);π if ChannelIsOn(Ch2) thenπ Writeln('Channel 2 is on!')π elseπ Writeln('Channel 2 is off!');πend.ππ 16 11-02-9305:04ALL DEREK POWLES BP7 Help file format SWAG9311 111 ~O {πDerek Powles <derek.powles@eng.ox.ac.uk>ππSubject: Re Help File formatππExpanded information on Help File sources.π In an earlier message I referred to four authors For the book in (1)πbelow - my mistake - there are three.π Derekππ(1) 'A Programmer's Guide to Turbo Vision' by Ertl, Machholz and Golgath,πauthored by the Pascal product management team at Borland in Germany.πPub. Addison-Wesley, ISBN 0-201-62401-X.πThe book contains chapter 12 ( 18 pages ) describing how to add aπconText sensitive help Unit to your own TV Program.πThe use of TVHC is described ( 2 pages ). TVHC generates helptxt.pas andπhelptxt.hlp Files from a special helptxt.txt File. The .pas File isπincluded in your main routine as a Unit. The HelpFile Unit is also needed,πthis Unit, TVHC and a working example are in the TVDEMO subdirectory.πThis .hlp File is not compatible With the Borland supplied Dos help Files,πthese have a name of the form *.t*h. I believe I am correct in stating that,πother than demohelp.hlp, all supplied *.hlp Files are For Windows use.ππ(2) The 'Borland Open Architecture Handbook For Pascal' describes andπsupplies the Program HL.EXE. Chapter 8 tells you '...how to create, build,πand maintain Borland Dos Help Files'. I have attached information gleanedπfrom this book.πThere are two sections, the first is information taken from the book andπthe second is a Unit holding data structures.ππlook For the *****cut here*****.ππ******************cut here*********************π{ See `Borland Open Architecture Handbook For Pascal' pages 170-177 }π(***************************************************************πBinary help File format -ππRecords are grouped in four sectionsππ 1 - File stampπ 2 - File signatureπ 3 - File versionπ 4 - Record Headersπ a - File header Recordπ b - conTextπ c - Textπ d - keyWordπ e - indexπ f - compression Recordπ g - indextags {= subheadings, = qualifiers}ππ ***************************************************************ππ 1 - A File stamp is a null terminated String, note case,π `TURBO PASCAL HelpFile.\0' orπ `TURBO C Help File.\0' identifying the File in readable form.π The null Character is followed by a Dos end-of-File Charcater $1A.π This is defined as ;STAMP in the help source File.ππ 2 - The File signature For Borland products is a null terminatedπ String `$*$* &&&&$*$' (no quotes in help Files).π This is defined as ;SIGNATURE in the help source File.ππ 3 - The File version is a Record of two Bytes that define the versionπ of help format and the help File Text respectively,ππ Typeπ TPVersionRec = Recordπ Formatversion : Byte;π TextVersion : Byte {defined With ;VERSION}π { this is For info only }π end;π FormatVersion For BP7 = $34π TP6 = $33π TP5 = $04π TP4 = $02π TC++3.0 = $04ππ 4 - Record Headers -π All the remaining Records have a common format that includes a headerπ identifying the Record's Type and length.ππ Typeπ TPrecHdr = Record;π RecType : Byte;π RecLength : Word;π end;ππ Field RecType is a code that identifies the Record Type.π Typeπ RecType =π (RT_FileHeader, {0}π RT_ConText, {1}π RT_Text, {2}π RT_KeyWord, {3}π RT_Index, {4}π RT_Compression); {5}π RT_IndexTags, {6 - only in FormatVersion $34}ππ Field RecLength gives the length of the contents of the Record inπ Bytes, not including the Record header. The contents begin With theπ first Byte following the header.ππ The field `RecType' Types are explained in the following Text.ππ Although this structure allows an arbitrary order of Records, existingπ Borland products assume a fixed Record ordering as follows:-π File header Recordπ compression Recordπ conText tableπ index tableπ indextags Record {introduced in BP7}π Text Recordπ keyWord Recordππ 4.a The File header Record defines Various parameters and optionsπ common to the help File.ππ Typeπ TPFileHdrRec = Recordπ Options : Word;π MainIndexScreen: Word;π Maxscreen size : Word;π Height : Byte;π Width : Byte;π LeftMargin : Byte;π end;ππ Options - is a bitmapped field. Only one option currentlyπ supported.π OF_CaseSense ($0004) (C only, not Pascal)π if set, index tokens are listed in mixed caseπ in the Index Record, and index searches willπ be Case sensitive.π if cleared, index tokens are all uppercase inπ the Index Record, and index searches will ignoreπ case.π Defined With ;CASESENSE.ππ MainIndexScreen - this is the number assigned to the conTextπ designated by the ;MAININDEX command in the helpπ source File.π if ;MAININDEX is not used, MainIndexScreen is setπ to zero.ππ MaxScreenSize - this is the number of Bytes in the longest Textπ Record in the File (not including the header). Thisπ field is not currently in use.ππ Height, Width - This is the default size in rows and columns,π respectively, of the display area of a help Window.π Defined With ;HEIGHT and ;WIDTH.ππ LeftMargin - Specifies the number of columns to leave blank onπ the left edge of all rows of help Text displayed.π Defined With ;LEFTMARGIN.ππ 4.b ConText table -π This is a table of Absolute File offsets that relates Help conTexts to theirπ associated Text. The first Word of the Record gives the number of conTextsπ in the table.π The remainder of the Record is a table of n ( n given by first Word) 3-Byteπ Integers (LSByte first). The table is indexed by conText number (0 to n-1).π The 3-Byte Integer at a given index is an Absolute Byte that is offset inπ the Help File where the Text of the associated conText begins.π The 3-Byte Integer is signed (2's complement).π Two special values are defined -π -1 use Index Screen Text (defined in File Header Record).π -2 no help available For this conText.π ConText table entry 0 is not used.ππ 4.c Text descriptions -π The Text Record defines the compressed Text of conText.π Text Records and keyWords appear in pairs, With one pair For eachπ conText in the File. The Text Record always precedes its associatedπ keyWord. Text Records are addressed through the File offset valuesπ found in the conText table described above.ππ The RecLength field of the Text Record header defines the numberπ of Bytes of compressed Text in the Record.π The Compression Record defines how the Text is compressed.π if the Text is nibble encoded, and the last nibble of the last Byteπ is not used, it is set to 0.π Lines of Text comprising the the Text Record are stored as nullπ terminated Strings.ππ 4.d the keyWord Record defines keyWords embedded in the preceeding Textπ Record, and identifies related Text Records.π The Record begins With the following fixed fields:π UpConText : Word;π DownConText : Word;π KeyWordCnt : Word;ππ UpConText and DownConText give the conText numbers of the previousπ and next sections of Text in a sequence, either may be zero,π indicating the end of the conText chain.ππ KeyWordCnt gives the number of keyWords encoded in the associatedπ Text Record.π Immediately following this field is an Array of KeyWord Descriptorπ Records of the following form:π Typeπ TPKwDesc = Record;π KwConText: Word;π end;ππ The keyWords in a Text Record are numbered from 1 to KeyWordCnt in theπ order they appear in the Text (reading left to right, top to bottom).ππ KwConText is a conText number (index into the conText table) indicatingπ which conText to switch to if this keyWord is selected by the user.ππ 4.e Index table -π This is a list of index descriptors.π An index is a token (normally a Word or name) that has been explicitlyπ associated With a conText using the ;INDEX command in the source Text File.π More than one index may be associated With a conText, but any given indexπ can not be associated With more than one conText.π The list of index descriptors in the Index Record allows the Text of anπ index token to be mapped into its associated conText number.ππ The first Word of the Record gives the number of indexes defined in theπ Record.π The remaining Bytes of the Record are grouped into index descriptors.π The descriptors are listed in ascending order based on the Text of the indexπ token (normal ascii collating sequence). if the OF_CaseSense flag is not setπ in the option field of the File header Record, all indexes are in uppercaseπ only.ππ Each index descriptor Uses the following format:π LengthCode : Byte;π UniqueChars : Array of Byte;π ConTextNumber : Word;ππ The bits of LengthCode are divided into two bit fields.π Bits(7..5) specify the number of Characters to carry over from the start ofπ the previous index token String.π Bits(4..0) specify the number of unique Characters to add to the end of theπ inherited Characters.ππ Field UniqueChars gives the number of unique Characters to add.π e.g. if the previous index token is `addition', and the next index token isπ `advanced', we inherit two Characters from the previous token (ad), and addπ six Characters (vanced); thus LengthCode would be $46.ππ ConTextNumber gives the number of the conText associated With the index.π This number is an index into the conText table described above.ππ 4.f A compression Record defines how the contents of Text Records areπ encoded.π Typeπ TPCompress = Recordπ CompType : Byte;π CharTable: Array[0..13] of Byte;π end;ππ CompType - nibble encoding is the only compression method currentlyπ in use.π Const CT_Nibble = 2;π The Text is encoded as a stream of nibbles. The nibbles are storedπ sequentially; the low nibble preceeds the high nibble of a Byte.π Nibble values ($0..$D) are direct indexes into the CharTable field ofπ the compression Record. The indexed Record is the literal Characterπ represented by the nibble. The Help Linker chooses the 14 (13?) mostπ frequent Characters For inclusion in this table. One exception is thatπ element 0 always maps to a Byte value of 0.ππ The remaining two nibble values have special meanings:π Constπ NC_RawChar = $F;π NC_RepChar = $E;ππ Nibble code NC_Char introduces two additional nibbles that define aπ literal Character; the least significant nibble first.ππ Nibble code NC_RepChar defines a Repeated sequence of a single Character.π The next nibble gives the Repeat count less two, (counts 2 to 17 areπ possible)π The next nibble(s) define the Character to Repeat; it can be either aπ single nibble in the range ($0..$D) representing an index intoπ CharTable, or it can be represented by a three nibble NC_RawCharπ sequence.πππ 4.g RT_IndexTags is in FormatVersion $34 only. This provides a meansπ For including index sub headings in the help File.π The Record header is followed by a list of Variable length tag Recordsπ Typeπ IndRecType = Record;π windexNumber: Word; {index into the RT_Index Record }π StrLen: Byte; {length of tag String(not including terminating 0)}π szTag: Array[0..0] of Char; {disable range checking}{zero term tagπ String}π end;π The first structure in the Array is a special entry which has theπ windexnumber set to $FFFF, and contains the default ;DESCRIPTIONπ in Case there are duplicate index entries and no tags were specified.π*)ππUnit HelpGlobals;π{ This File contains only the structures which are found in the help Files }ππInterfaceππConstπ Signature = '$*$* &&&&*$'; {+ null terminator }π NC_RawChar = $F;π NC_RepChar = $E;ππTypeπ FileStamp = Array [0..32] Of Char; {+ null terminator + $1A }π FileSignature = Array [0..12] Of Char; {+ null terminator }ππ TPVersion = Recordπ FormatVersion : Byte;π TextVersion : Byte;π end;ππ TPRecHdr = Recordπ RecType : Byte; {TPRecType}π RecLength : Word;π end;ππConst {RecType}π RT_FileHeader = Byte ($0);π RT_ConText = Byte ($1);π RT_Text = Byte ($2);π RT_KeyWord = Byte ($3);π RT_Index = Byte ($4);π RT_Compression = Byte ($5);π RT_IndexTags = Byte ($6);πππTypeπ TPIndRecType = Recordπ windexNumber : Word;π StrLen : Byte;π szTag : Array [0..0] Of Char;π { disable range checking}π end;ππ TPFileHdrRec = Recordπ Options : Word;π MainIndexScreen : Word;π Maxscreensize : Word;π Height : Byte;π Width : Byte;π LeftMargin : Byte;π end;πππ TP4FileHdrRec = Record {derived from sample File}π Options : Word;π Height : Byte;π Width : Byte;π LeftMargin : Byte;π end;ππ TPCompress = Recordπ CompType : Byte;π CharTable : Array [0..13] Of Byte;π end;ππ TPIndexDescriptor = Recordπ LengthCode : Byte;π UniqueChars : Array [0..0] Of Byte;π ConTextNumber : Word;π end;ππ TPKeyWord = Recordπ UpConText : Word;π DownConText : Word;π KeyWordCnt : Word;π end;ππ TPKwDesc = Recordπ KwConText : Word;π end;πππ TmyStream = Object(TBufStream) end;ππ PListRecHdr = ^RListRecHdr;ππ RListRecHdr = Recordπ PNextHdr : PListRecHdr; {Pointer to next list element}π RRecType : TPRecHdr; {RecType, RecLength}π PRecPtr : Pointer; {Pointer to copy of Record}π end;ππ ConTextRecHd = Recordπ NoConTexts : Word;π PConTextRec : Pointer;π end;πππImplementationπend.ππ 17 11-02-9306:02ALL FRED JOHNSON Accessing locals in BASM SWAG9311 7 ~O {πRAPHAEL VANNEYππ> I've got a question about BAsm: How would I go about accessing a localπ> Variable in an assembly block? I know that locals are stored on theπ> stack: Var temp:Byte;π}ππProcedure TestLocal(Var a : Integer); Assembler;πVarπ i : Byte;π Stri : String;πAsmπ { Getting Pointers... }π Push SSπ Pop ESπ LEA SI, i { ES:SI points to i }π LEA DI, Stri { ...and ES:DI points to Stri }ππ { if you Really need DS as a segment... }π Push DS { Save DS }π Mov AX, SS { Copy SS to AX... }π Mov DS, AX { ...then to DS }π LEA DX, Stri { DS:DX points to Stri }π Pop DS { Restore DS }ππ LES DX, a { ES:DX points to a }ππ { Now using local Vars }π Inc iπ Mov i, 10π { etc... }πend;ππ 18 11-02-9305:45ALL HERBERT ZARB GIF File format SWAG9311 222 ~O Herbert Zarb <panther!jaguar!hzarb@relay.iunet.it>ππ This Text File explains the format of Gif Files.ππ---------------------------------------------------------------------------π G I F (tm)π Graphics Interchange Format (tm)ππ A standard defining a mechanismπ For the storage and transmissionπ of raster-based Graphics informationππ June 15, 1987ππ (c) CompuServe Incorporated, 1987π All rights reservedππ While this document is copyrighted, the informationπ contained Within is made available For use in computerπ software Without royalties, or licensing restrictions.ππ Gif and 'Graphics Interchange Format' are trademarks ofπ CompuServe, Incorporated.π an H&R Block Companyππ 5000 Arlington Centre Blvd.π Columbus, Ohio 43220π (614) 457-8600π Page 2πππ Graphics Interchange Format (GIF) Specificationπππ Table of Contentsππ INTRODUCTION . . . . . . . . . . . . . . . . . page 3π GENERAL File FORMAT . . . . . . . . . . . . . page 3π Gif SIGNATURE . . . . . . . . . . . . . . . . page 4π SCREEN DESCRIPTOR . . . . . . . . . . . . . . page 4π GLOBAL COLOR MAP . . . . . . . . . . . . . . . page 5π IMAGE DESCRIPTOR . . . . . . . . . . . . . . . page 6π LOCAL COLOR MAP . . . . . . . . . . . . . . . page 7π RASTER DATA . . . . . . . . . . . . . . . . . page 7π Gif TERMINATOR . . . . . . . . . . . . . . . . page 8π Gif EXTENSION BLOCKS . . . . . . . . . . . . . page 8π APPendIX A - GLOSSARY . . . . . . . . . . . . page 9π APPendIX B - INTERACTIVE SEQUENCES . . . . . . page 10π APPendIX C - IMAGE PACKAGING & COMPRESSION . . page 12π APPendIX D - MULTIPLE IMAGE PROCESSING . . . . page 15πππINTRODUCTIONππ 'GIF' (tm) is CompuServe's standard For defining generalized colorπ raster images. This 'Graphics Interchange Format' (tm) allowsπ high-quality, high-resolution Graphics to be displayed on a Variety ofπ Graphics hardware and is intended as an exchange and display mechanismπ For Graphics images. The image format described in this document isπ designed to support current and future image technology and will inπ addition serve as a basis For future CompuServe Graphics products.ππ The main focus of this document is to provide the technicalπ information necessary For a Programmer to implement Gif encoders andπ decoders. As such, some assumptions are made as to terminology relaventπ to Graphics and Programming in general.ππ The first section of this document describes the Gif data formatπ and its components and applies to all Gif decoders, either as standaloneπ Programs or as part of a communications package. Appendix B is aπ section relavent to decoders that are part of a communications softwareπ package and describes the protocol requirements For entering and Exitingπ Gif mode, and responding to host interrogations. A glossary in Appendixπ A defines some of the terminology used in this document. Appendix Cπ gives a detailed explanation of how the Graphics image itself isπ packaged as a series of data Bytes.πππ Graphics Interchange Format Data Definitionπππ GENERAL File FORMATππ +-----------------------+π | +-------------------+ |π | | Gif Signature | |π | +-------------------+ |π | +-------------------+ |π | | Screen Descriptor | |π | +-------------------+ |π | +-------------------+ |π | | Global Color Map | |π | +-------------------+ |π . . . . . .π | +-------------------+ | ---+π | | Image Descriptor | | |π | +-------------------+ | |π | +-------------------+ | |π | | Local Color Map | | |- Repeated 1 to n timesπ | +-------------------+ | |π | +-------------------+ | |π | | Raster Data | | |π | +-------------------+ | ---+π . . . . . .π |- Gif Terminator -|π +-----------------------+πππ Gif SIGNATUREππ The following Gif Signature identifies the data following as aπ valid Gif image stream. It consists of the following six Characters:ππ G I F 8 7 aππ The last three Characters '87a' may be viewed as a version numberπ For this particular Gif definition and will be used in general as aπ reference in documents regarding Gif that address any versionπ dependencies.ππ SCREEN DESCRIPTORππ The Screen Descriptor describes the overall parameters For all GIFπ images following. It defines the overall dimensions of the image spaceπ or logical screen required, the existance of color mapping information,π background screen color, and color depth information. This informationπ is stored in a series of 8-bit Bytes as described below.ππ bitsπ 7 6 5 4 3 2 1 0 Byte #π +---------------+π | | 1π +-Screen Width -+ Raster width in pixels (LSB first)π | | 2π +---------------+π | | 3π +-Screen Height-+ Raster height in pixels (LSB first)π | | 4π +-+-----+-+-----+ M = 1, Global color map follows Descriptorπ |M| cr |0|pixel| 5 cr+1 = # bits of color resolutionπ +-+-----+-+-----+ pixel+1 = # bits/pixel in imageπ | background | 6 background=Color index of screen backgroundπ +---------------+ (color is defined from the Global colorπ |0 0 0 0 0 0 0 0| 7 map or default map if none specified)π +---------------+πππ The logical screen width and height can both be larger than theπ physical display. How images larger than the physical display areπ handled is Implementation dependent and can take advantage of hardwareπ Characteristics (e.g. Macintosh scrolling Windows). Otherwise imagesπ can be clipped to the edges of the display.ππ The value of 'pixel' also defines the maximum number of colorsπ Within an image. The range of values For 'pixel' is 0 to 7 whichπ represents 1 to 8 bits. This translates to a range of 2 (B & W) to 256π colors. Bit 3 of Word 5 is reserved For future definition and must beπ zero.πππ GLOBAL COLOR MAPππ The Global Color Map is optional but recommended For images whereπ accurate color rendition is desired. The existence of this color map isπ indicated in the 'M' field of Byte 5 of the Screen Descriptor. A colorπ map can also be associated With each image in a Gif File as describedπ later. However this global map will normally be used because ofπ hardware restrictions in equipment available today. In the individualπ Image Descriptors the 'M' flag will normally be zero. if the Globalπ Color Map is present, it's definition immediately follows the Screenπ Descriptor. The number of color map entries following a Screenπ Descriptor is equal to 2**(# bits per pixel), where each entry consistsπ of three Byte values representing the relative intensities of red, greenπ and blue respectively. The structure of the Color Map block is:ππ bitsπ 7 6 5 4 3 2 1 0 Byte #π +---------------+π | red intensity | 1 Red value For color index 0π +---------------+π |green intensity| 2 Green value For color index 0π +---------------+π | blue intensity| 3 Blue value For color index 0π +---------------+π | red intensity | 4 Red value For color index 1π +---------------+π |green intensity| 5 Green value For color index 1π +---------------+π | blue intensity| 6 Blue value For color index 1π +---------------+π : : (Continues For remaining colors)ππ Each image pixel value received will be displayed according to itsπ closest match With an available color of the display based on this colorπ map. The color components represent a fractional intensity value fromπ none (0) to full (255). White would be represented as (255,255,255),π black as (0,0,0) and medium yellow as (180,180,0). For display, if theπ device supports fewer than 8 bits per color component, the higher orderπ bits of each component are used. In the creation of a Gif color mapπ entry With hardware supporting fewer than 8 bits per component, theπ component values For the hardware should be converted to the 8-bitπ format With the following calculation:ππ <map_value> = <component_value>*255/(2**<nbits> -1)ππ This assures accurate translation of colors For all displays. Inπ the cases of creating Gif images from hardware Without color paletteπ capability, a fixed palette should be created based on the availableπ display colors For that hardware. if no Global Color Map is indicated,π a default color map is generated internally which maps each possibleπ incoming color index to the same hardware color index modulo <n> whereπ <n> is the number of available hardware colors.πππ IMAGE DESCRIPTORππ The Image Descriptor defines the actual placement and extents ofπ the following image Within the space defined in the Screen Descriptor.π Also defined are flags to indicate the presence of a local color lookupπ map, and to define the pixel display sequence. Each Image Descriptor isπ introduced by an image separator Character. The role of the Imageπ Separator is simply to provide a synchronization Character to introduceπ an Image Descriptor. This is desirable if a Gif File happens to containπ more than one image. This Character is defined as 0x2C hex or ','π (comma). When this Character is encountered between images, the Imageπ Descriptor will follow immediately.ππ Any Characters encountered between the end of a previous image andπ the image separator Character are to be ignored. This allows future GIFπ enhancements to be present in newer image formats and yet ignored safelyπ by older software decoders.πππ bitsπ 7 6 5 4 3 2 1 0 Byte #π +---------------+π |0 0 1 0 1 1 0 0| 1 ',' - Image separator Characterπ +---------------+π | | 2 Start of image in pixels from theπ +- Image Left -+ left side of the screen (LSB first)π | | 3π +---------------+π | | 4π +- Image Top -+ Start of image in pixels from theπ | | 5 top of the screen (LSB first)π +---------------+π | | 6π +- Image Width -+ Width of the image in pixels (LSB first)π | | 7π +---------------+π | | 8π +- Image Height-+ Height of the image in pixels (LSB first)π | | 9π +-+-+-+-+-+-----+ M=0 - Use global color map, ignore 'pixel'π |M|I|0|0|0|pixel| 10 M=1 - Local color map follows, use 'pixel'π +-+-+-+-+-+-----+ I=0 - Image formatted in Sequential orderπ I=1 - Image formatted in Interlaced orderπ pixel+1 - # bits per pixel For this imageππ The specifications For the image position and size must be confinedπ to the dimensions defined by the Screen Descriptor. On the other handπ it is not necessary that the image fill the entire screen defined.πππ LOCAL COLOR MAPππ A Local Color Map is optional and defined here For future use. Ifπ the 'M' bit of Byte 10 of the Image Descriptor is set, then a color mapπ follows the Image Descriptor that applies only to the following image.π At the end of the image, the color map will revert to that defined afterπ the Screen Descriptor. Note that the 'pixel' field of Byte 10 of theπ Image Descriptor is used only if a Local Color Map is indicated. Thisπ defines the parameters not only For the image pixel size, but determinesπ the number of color map entries that follow. The bits per pixel valueπ will also revert to the value specified in the Screen Descriptor whenπ processing of the image is complete.ππ RASTER DATAππ The format of the actual image is defined as the series of pixelπ color index values that make up the image. The pixels are stored leftπ to right sequentially For an image row. By default each image row isπ written sequentially, top to bottom. In the Case that the Interlace orπ 'I' bit is set in Byte 10 of the Image Descriptor then the row order ofπ the image display follows a four-pass process in which the image isπ filled in by widely spaced rows. The first pass Writes every 8th row,π starting With the top row of the image Window. The second pass Writesπ every 8th row starting at the fifth row from the top. The third passπ Writes every 4th row starting at the third row from the top. The fourthπ pass completes the image, writing every other row, starting at theπ second row from the top. A Graphic description of this process follows:πππ Imageπ Row Pass 1 Pass 2 Pass 3 Pass 4 Resultπ ---------------------------------------------------π 0 **1a** **1a**π 1 **4a** **4a**π 2 **3a** **3a**π 3 **4b** **4b**π 4 **2a** **2a**π 5 **4c** **4c**π 6 **3b** **3b**π 7 **4d** **4d**π 8 **1b** **1b**π 9 **4e** **4e**π 10 **3c** **3c**π 11 **4f** **4f**π 12 **2b** **2b**π . . .ππππ The image pixel values are processed as a series of color indicesπ which map into the existing color map. The resulting color value fromπ the map is what is actually displayed. This series of pixel indices,π the number of which is equal to image-width*image-height pixels, areπ passed to the Gif image data stream one value per pixel, compressed andπ packaged according to a version of the LZW compression algorithm asπ defined in Appendix C.ππ Gif TERMINATORππ In order to provide a synchronization For the termination of a GIFπ image File, a Gif decoder will process the end of Gif mode when theπ Character 0x3B hex or ';' is found after an image has been processed.π By convention the decoding software will pause and wait For an actionπ indicating that the user is ready to continue. This may be a carriageπ return entered at the keyboard or a mouse click. For interactiveπ applications this user action must be passed on to the host as aπ carriage return Character so that the host application can continue.π The decoding software will then typically leave Graphics mode and resumeπ any previous process.πππ Gif EXTENSION BLOCKSππ To provide For orderly extension of the Gif definition, a mechanismπ For defining the packaging of extensions Within a Gif data stream isπ necessary. Specific Gif extensions are to be defined and documented byπ CompuServe in order to provide a controlled enhancement path.ππ Gif Extension Blocks are packaged in a manner similar to that usedπ by the raster data though not compressed. The basic structure is:ππ 7 6 5 4 3 2 1 0 Byte #π +---------------+π |0 0 1 0 0 0 0 1| 1 '!' - Gif Extension Block Introducerπ +---------------+π | Function code | 2 Extension Function code (0 to 255)π +---------------+ ---+π | Byte count | |π +---------------+ |π : : +-- Repeated as many times as necessaryπ |func data Bytes| |π : : |π +---------------+ ---+π . . . . . .π +---------------+π |0 0 0 0 0 0 0 0| zero Byte count (terminates block)π +---------------+ππ A Gif Extension Block may immediately preceed any Image Descriptorπ or occur before the Gif Terminator.ππ All Gif decoders must be able to recognize the existence of GIFπ Extension Blocks and read past them if unable to process the Functionπ code. This ensures that older decoders will be able to process extendedπ Gif image Files in the future, though Without the additionalπ Functionality.πππ GLOSSARYππPixel - The smallest picture element of a Graphics image. This usuallyπ corresponds to a single dot on a Graphics screen. Image resolution isπ typically given in Units of pixels. For example a fairly standardπ Graphics screen format is one 320 pixels across and 200 pixels high.π Each pixel can appear as one of several colors depending on theπ capabilities of the Graphics hardware.ππRaster - A horizontal row of pixels representing one line of an image. Aπ typical method of working With images since most hardware is oriented toπ work most efficiently in this manner.ππLSB - Least Significant Byte. Refers to a convention For two Byte numericπ values in which the less significant Byte of the value preceeds the moreπ significant Byte. This convention is typical on many microcomputers.ππColor Map - The list of definitions of each color used in a Gif image.π These desired colors are converted to available colors through a tableπ which is derived by assigning an incoming color index (from the image)π to an output color index (of the hardware). While the color mapπ definitons are specified in a Gif image, the output pixel colors willπ Vary based on the hardware used and its ability to match the definedπ color.ππInterlace - The method of displaying a Gif image in which multiple passesπ are made, outputting raster lines spaced apart to provide a way ofπ visualizing the general content of an entire image before all of theπ data has been processed.ππB Protocol - A CompuServe-developed error-correcting File transfer protocolπ available in the public domain and implemented in CompuServe VIDTEXπ products. This error checking mechanism will be used in transfers ofπ Gif images For interactive applications.ππLZW - A sophisticated data compression algorithm based on work done byπ Lempel-Ziv & Welch which has the feature of very efficient one-passπ encoding and decoding. This allows the image to be decompressed andπ displayed at the same time. The original article from which thisπ technique was adapted is:ππ Terry A. Welch, "A Technique For High Performance Dataπ Compression", IEEE Computer, vol 17 no 6 (June 1984)ππ This basic algorithm is also used in the public domain ARC Fileπ compression utilities. The CompuServe adaptation of LZW For Gif isπ described in Appendix C.ππ Gif Sequence Exchanges For an Interactive Environmentπππ The following sequences are defined For use in mediating controlπ between a Gif sender and Gif receiver over an interactive communicationsπ line. These sequences do not apply to applications that involveπ downloading of static Gif Files and are not considered part of a GIFπ File.ππ Gif CAPABILITIES ENQUIRYππ The GCE sequence is issued from a host and requests an interactiveπ Gif decoder to return a response message that defines the Graphicsπ parameters For the decoder. This involves returning information aboutπ available screen sizes, number of bits/color supported and the amount ofπ color detail supported. The escape sequence For the GCE is defined as:ππ ESC [ > 0 g (g is lower case, spaces inserted For clarity)π (0x1B 0x5B 0x3E 0x30 0x67)πππ Gif CAPABILITIES RESPONSEππ The Gif Capabilities Response message is returned by an interactiveπ Gif decoder and defines the decoder's display capabilities For allπ Graphics modes that are supported by the software. Note that this canπ also include Graphics Printers as well as a monitor screen. The generalπ format of this message is:πππ #version;protocol{;dev, width, height, color-bits, color-res}... <CR>ππ '#' - GCR identifier Character (Number Sign)π version - Gif format version number; initially '87a'π protocol='0' - No end-to-end protocol supported by decoderπ Transfer as direct 8-bit data stream.π protocol='1' - Can use an error correction protocol to transfer Gif dataπ interactively from the host directly to the display.ππ dev = '0' - Screen parameter set followsπ dev = '1' - Printer parameter set followsππ width - Maximum supported display width in pixelsπ height - Maximum supported display height in pixelsπ color-bits - Number of bits per pixel supported. The number ofπ supported colors is therefore 2**color-bits.π color-res - Number of bits per color component supported in theπ hardware color palette. if color-res is '0' then noπ hardware palette table is available.πππ Note that all values in the GCR are returned as ASCII decimalπ numbers and the message is terminated by a Carriage Return Character.ππ The following GCR message describes three standard EGAπ configurations With no Printer; the Gif data stream can be processedπ Within an error correcting protocol:ππ #87a;1 ;0,320,200,4,0 ;0,640,200,2,2 ;0,640,350,4,2<CR>πππ ENTER Gif GraphICS MODEππ Two sequences are currently defined to invoke an interactive GIFπ decoder into action. The only difference between them is that differentπ output media are selected. These sequences are:ππ ESC [ > 1 g Display Gif image on screenπ (0x1B 0x5B 0x3E 0x31 0x67)ππ ESC [ > 2 g Display image directly to an attached Graphics Printer.π The image may optionally be displayed on the screen asπ well.π (0x1B 0x5B 0x3E 0x32 0x67)ππ Note that the 'g' Character terminating each sequence is in lowerπ case.πππ INTERACTIVE ENVIRONMENTππ The assumed environment For the transmission of Gif image data fromπ an interactive application is a full 8-bit data stream from host toπ micro. All 256 Character codes must be transferrable. The establishingπ of an 8-bit data path For communications will normally be taken care ofπ by the host application Programs. It is however up to the receivingπ communications Programs supporting Gif to be able to receive and pass onπ all 256 8-bit codes to the Gif decoder software.ππ The Raster Data stream that represents the actual output image canπ be represented as:ππ 7 6 5 4 3 2 1 0π +---------------+π | code size |π +---------------+ ---+π |blok Byte count| |π +---------------+ |π : : +-- Repeated as many times as necessaryπ | data Bytes | |π : : |π +---------------+ ---+π . . . . . .π +---------------+π |0 0 0 0 0 0 0 0| zero Byte count (terminates data stream)π +---------------+ππ The conversion of the image from a series of pixel values to aπ transmitted or stored Character stream involves several steps. In briefπ these steps are:ππ 1. Establish the Code Size - Define the number of bits needed toπ represent the actual data.ππ 2. Compress the Data - Compress the series of image pixels to a seriesπ of compression codes.ππ 3. Build a Series of Bytes - Take the set of compression codes andπ convert to a String of 8-bit Bytes.ππ 4. Package the Bytes - Package sets of Bytes into blocks preceeded byπ Character counts and output.ππππESTABLISH CODE SIZEππ The first Byte of the Gif Raster Data stream is a value indicatingπ the minimum number of bits required to represent the set of actual pixelπ values. Normally this will be the same as the number of color bits.π Because of some algorithmic Constraints however, black & white imagesπ which have one color bit must be indicated as having a code size of 2.π This code size value also implies that the compression codes must startπ out one bit longer.πππCOMPRESSIONππ The LZW algorithm converts a series of data values into a series ofπ codes which may be raw values or a code designating a series of values.π Using Text Characters as an analogy, the output code consists of aπ Character or a code representing a String of Characters.ππ The LZW algorithm used in Gif matches algorithmically With theπ standard LZW algorithm With the following differences:ππ 1. A special Clear code is defined which resets allπ compression/decompression parameters and tables to a start-up state.π The value of this code is 2**<code size>. For example if the codeπ size indicated was 4 (image was 4 bits/pixel) the Clear code valueπ would be 16 (10000 binary). The Clear code can appear at any pointπ in the image data stream and therefore requires the LZW algorithm toπ process succeeding codes as if a new data stream was starting.π Encoders should output a Clear code as the first code of each imageπ data stream.ππ 2. An end of Information code is defined that explicitly indicates theπ end of the image data stream. LZW processing terminates when thisπ code is encountered. It must be the last code output by the encoderπ For an image. The value of this code is <Clear code>+1.ππ 3. The first available compression code value is <Clear code>+2.ππ 4. The output codes are of Variable length, starting at <code size>+1π bits per code, up to 12 bits per code. This defines a maximum codeπ value of 4095 (hex FFF). Whenever the LZW code value would exceedπ the current code length, the code length is increased by one. Theπ packing/unpacking of these codes must then be altered to reflect theπ new code length.πππBUILD 8-BIT ByteSππ Because the LZW compression used For Gif creates a series ofπ Variable length codes, of between 3 and 12 bits each, these codes mustπ be reformed into a series of 8-bit Bytes that will be the Charactersπ actually stored or transmitted. This provides additional compression ofπ the image. The codes are formed into a stream of bits as if they wereπ packed right to left and then picked off 8 bits at a time to be output.π Assuming a Character Array of 8 bits per Character and using 5 bit codesπ to be packed, an example layout would be similar to:ππ Byte n Byte 5 Byte 4 Byte 3 Byte 2 Byte 1π +-.....-----+--------+--------+--------+--------+--------+π | and so on |hhhhhggg|ggfffffe|eeeedddd|dcccccbb|bbbaaaaa|π +-.....-----+--------+--------+--------+--------+--------+ππ Note that the physical packing arrangement will change as theπ number of bits per compression code change but the concept remains theπ same.ππPACKAGE THE ByteSππ Once the Bytes have been created, they are grouped into blocks forπ output by preceeding each block of 0 to 255 Bytes With a Character countπ Byte. A block With a zero Byte count terminates the Raster Data streamπ For a given image. These blocks are what are actually output For theπ Gif image. This block format has the side effect of allowing a decodingπ Program the ability to read past the actual image data if necessary byπ reading block counts and then skipping over the data.πGraphics Interchange Format (GIF) Page 15πAppendix D - Multiple Image Processingπππ Since a Gif data stream can contain multiple images, it isπ necessary to describe processing and display of such a File. Becauseπ the image descriptor allows For placement of the image Within theπ logical screen, it is possible to define a sequence of images that mayπ each be a partial screen, but in total fill the entire screen. Theπ guidelines For handling the multiple image situation are:ππ 1. There is no pause between images. Each is processed immediately asπ seen by the decoder.ππ 2. Each image explicitly overWrites any image already on the screenπ inside of its Window. The only screen clears are at the beginningπ and end of the Gif image process. See discussion on the GIFπ terminator.ππ 19 11-02-9306:07ALL JOHN GUILLORY More MIDI Information SWAG9311 25 ~O {πJOHN GUILLORYππ> - the UART 8250, the chip which is their I/O controllerπI don't think they use an 8250 UART chip, yet they are similar. the MIDIπUART is supposed to be faster than the 8250.ππ> - the MIDI protocol ( as far as there is one ... )πBasically it's like this: Commands start at 80h the lower 4 bits (nibble)πdesignate the channel number such that 0 = Channel 1, 0f = 16. Once aπcommand is given, it is assumed that that command is in effect Until anotherπcommand has been given. eg.ππ$C0 is a command to change the Program number, and I think 80 is note on.πif so, an example from the MIDI would be:ππ80 33 60 80 33 00 C0 02 80 33 60 80 33 00ππwhere the 33 is the note to play For the command 80h and 60 is theπpressure/volume (non-presure sensitive keyboards send 64 For the pressure)πNotes are in the order of a keyboard e.g. 0 would tech. be the first C onπthe keyboard, 1 would be a C sharp, 2 = D ..., Although most keyboardsπ(unless they're their enormous) start w/Middle C at around 36 or so, and anyπkey above middle C is that much above 36, any key below middle C is thatπmuch below.... in MIDI, you can add an octive to a note by adding 12,πsubtract 12 to lower it an octive....ππTo setup keyboards, you can send a System Request command (think its F0 orπsomething like that...) then an ID and a series of Bytes. The ID Designatesπthe manufacturer of the keyboard, such that only the devices With that IDπwill respond to that event.ππSeek the Electronic Musician Magazine, May 1989 I'm told gives an article onπhandling the MPU-401 interrupts, as well as lots of source code that I usedπto have on the MPU-401 seems to come from this magazine.πππThe MIDI is quite easy to Program compared to the Sound blaster where youπhave to count of so many clock-tick's etc. the MPU-401 is pretty much aπ'check and see if we can send, then do it...' Type card. Certain commandsπdo however take a little time For the devices to process eg. change a Programπ# it takes so many ms. For that device to be ready For another command, playπa note, a few ms. before the next one...ππThis can become frustation before you learn how to use it... (I never couldπfind out why it'd change the first Program # but none of the rest...<grin>)ππI/O Address 330h is the Std. (though can change on some MPU-401's) I/O Portπfor Data. I/O Address 331 is the Status/Comport.ππReading the Status port (331h) and masking 80h will tell you if something isπwaiting to be received from the mpu-401. e.g.π}ππFunction Receive_MPU(Var B : Byte) : Boolean;πbeginπ if (Port[$331] and $80) = 0 thenπ beginπ B := Port[$330];π Receive_MPU := True;π endπ elseπ Receive_MPU := False;πend;ππ{πTo Send a Command to the MPU, you must wait till there's no data in theπbuffer... The original code I used to have would flush the data if it wasπfor some reason present when you'd send a Byte... here's a rough example ofπsending data...π}ππProcedure Send_MPU(B : Byte);πbeginπ Repeat Until (Port[$331] and $80) = 0;π Port[$330] := B;πend;ππ 20 11-02-9306:12ALL MARK OUELLET Reading Array from REGS SWAG9311 10 ~O {πMARK OUELLETππ> How can I read what appears to be an Array from the Registers valueπ> (this is after making the interrupt call, and is returned Withπ> information... I'll be durned if I know how to use it):ππ> values upon returnπ> AX = clear on successful (or whatever ... not important)π> ES:DX = see table 2.1π>π> table 2.1π> offset - info (size)π> -----------------------------π> 00h - blah blah (4 Bytes)π> 03h - blah blah (16 Bytes)π> etc ....π>π> And the ES:DX usually points to what appears to be a Record, or a bufferπ> of data using an offset to identify what's what. How can I use and/orπ> access this info?π}ππ Typeπ TablePtr = ^Tableπ Table = Recordπ BlahBlah1 : LongInt; { 4Bytes }π BlahBlah2 : Array[1..16] of Byte;π .π .π etc....π end;π{π if using Intr() or MSDos() and the Registers structure defined inπDos.tpu then:π}πVarπ Regs : Registers; {Defined in Dos.tpu}π MyTable : TablePtr;ππbeginπ Regs.AX := ??;π Regs.BX := ??;π Intr(Regs);π TablePtr := Ptr(Regs.ES, Regs.DX);ππ Write(TablePtr^.BlahBlah1);π .π .π etc...πππ 21 11-02-9305:27ALL ROBERT ROTHENBURG Ins and Outs of Compress SWAG9311 100 ~O {πRobert RothenburgππOk, since a few people have requested info about compression routines Iπthought some actual descriptions of the algorithm(s) involved would be aπgood idea. Rather than dig out someone else's text file or mail (whichπmight be copyrighted or arcane) I'll try my hand at explaining a fewπmethods.ππIt seems better that programmers have at least a rudimentary understandingπof the algortihms if they plan on using (or not using :) them.ππDISCLAIMER: Please pardon any innacuracies: What I know is based on otherπ people's mail, text files or from what I've gleaned from spending a fewπ hours at the library (adivce: your local college research-library is aπ wonderful resource of algorithms and source-code. Old magazines as wellπ as academic papers like the IEEE Transactions are worth examining).πππIn this insanely long post:ππI. "Garbage Collection"πII. "Keywords"πIII. Run-Length Encoding (RLE)πIV. Static and Dynamic Huffmann Codes <--(BTW, is it one or two n's?)πV. Lampev-Ziv (LZ)πVI. Lampev-Ziv-Welch (LZW) et al.πππI. "Garbage Collection"ππ The simplest methods of compression in weeding out unnecessary data,π especially from text files. Spaces at the end of a line, or extraπ carriage returns at the end of a file. (Especially with MS-DOS textπ files, which use a Carriage Return *and* Line Feed--many editors andπ file-browsers do not need both. Eliminating one of them can cut a largeπ text file's size by a few percent!)ππ I've also seen some utilities that "clean up" the headers of EXEπ files (such as UNP) by eliminating `unnecessary' info. Otherπ utilities will clean up graphics files so that they'll compressπ better.ππ In fact, removing excess "garbage" from any file will probablyπ improve its compressability.ππII. "Keywords"ππ Another way to compress text files is to use a "keyword" for each word.ππ I.E. we can assume most text files will have less than 65,536 differentπ words (16 bits=two bytes), and thus we can assign a different value forπ each word: since most words are longer than three letters (more than twoπ bytes), we will save space in the file. However, we'll need some form ofπ look-up table for each keyword--one way around this might be to use aπ reference to a standard dictionary file that is included with an operatingπ system or word processor.ππ (This has other advantages as well. Many BASIC interpreters will storeπ commands like PRINT or INPUT as a character code rather than the entireπ name in ASCII text. Not only does this save memory but improves theπ run-speed of the program.)ππ This method can be adapted to other (non-text) files, of course.ππIII. Run-Length Encoding (RLE)ππ With RLE, repeated characters (such as leading spaces in text, or largeπ areas of one color in graphics) are expressed with a code noting whichπ byte is repeated and how many times it's repeated.ππIV. Static and Dynamic Huffmann Codesππ The logic behind this method is that certain characters occur more oftenπ than others, and thus the more common characters can be expressed withπ fewer bits. For example, take a text file: 'e' might be the most commonπ character, then 'a', then 's', then 'i', etc....ππ We can express these characters in a bit-stream like so:ππ 'e' = 1π 'a' = 01 <--These are Static Huffmann Codes.π 's' = 001π 'i' = 0001π etc...ππ Since these characters normally take up 8-bits, the first (most common)π seven will save space when expressed this way, if they occur often enoughπ in relation to the other 249 characters.ππ This is often represented as a (Static) Huffmann Tree:ππ /\ Note that a compression routineπ 'e' = 1 0 will have to first scan the dataπ / \ and count which characters areπ 'a' = 1 0 more common and assign the codesπ / \ appropriately.π etc... 1 0π / \π etc...ππ Notice that if we use the full 256 ASCII characters we'll run intoπ a problem with long strings of 0's. We can get around that by usingπ RLE (Which is what the compressor SQUEEZE uses).ππ Again, since most files use a large range of the character set, andπ their occurences are much more "even", we can use use Dynamic Huffmannπ Codes as an alternative. They are like their static cousins, only afterπ the string of n zeros they are followed by n (binary) digits:ππ 1 = 1 1 characterπ 01x = 010, 011 2 charsπ 001xx = 00100, 00101, 00110, 0011 4 charsπ 0001xxx = 0001000, 0001001 ... 0001111 8 charsπ etc...ππ As you can guess, the Huffmann Tree would look something like:ππ /\ It's a bit too complicated toπ 1 0 express with ASCII <g>π / \π 1 0π / \ /\π 1 0 etc...ππ Huffmann Coding is based on how often an item (such as an ASCIIπ character) occurs, and not where or in relation to other items.π It's not the msot efficient mothod, but it is one of the simplest.π One only needs to make an initial pass to count the characters andπ then re-pass translating them into the appropriate codes. Noπ pattern-matching or search routines are required.ππV. Lampev-Ziv (LZ) Encoding.ππ This method _does_ require some fast pattern-matching/search routines.π It takes advantage of the fact that in most files (especially text)π whole strings of characters repeat themselves.ππ Take this sample line of text:ππ "THAT WHICH IS, IS. THAT WHICH IS NOT, IS NOT. IS IT? IT IS!"ππ (Ok, so "Flowers for Algernon" was on TV the other night... :)π With LZ, we read in from the beginning of the file and keep addingπ groups ("Windows") of characters that have not already occurred.π So we add the first sentence, then get to the second "IS"...insteadπ of repeating it we note that it's a duplicate, with a referenceπ to where the original occurred and how long the string is. (I.E. we noteπ that the "IS" occurred 4 characters previously, and is two charactersπ long.)ππ This method can be tweaked by using a "Sliding Window" approach where theπ largest matches are found...we can compress the second "IS" with the first,π but when don't gain much. However the two "IS NOT"s, when matched againstπ each other, would compress better.ππ Our compressed file will have two types of data: the raw characters andπ the LZ-references (containing the pointer and length) to the rawπ characters, or even to other LZ-references.ππ One way to tweak this further is to compress the LZ-References usingπ Huffmann codes. (I think this is what's done with the ZIP algorithm.)ππVI. Lampev-Ziv-Welch (LZW) -- Not to be confused with LZ compression.ππ This is the method used by Unix COMPRESS, as well as the GIF-graphicsπ file format.ππ Like LZ, LZW compression can compress a stream of data on one-passπ relatively quickly (assuming you've got the fast search routines!).π However, LZW uses a hash table (essentially it's an array of strings,π also known as a string table), and a "match" is expressed as its indexπ in the hash table rather than a reference to where and how long theπ match is.ππ The input stream is read in, strings are assembled and sent out whenπ they are already in the hash table, or they are added to the hash tableπ and sent out in such a way that a decoder can still re-assemble theπ file.ππ Needless to say, this method is a memory hog. Most compressors thatπ use this method are usually limited to a certain number of entriesπ in the hash-table (12- or 16-bits, or 4096 and 65536 respectively).ππ Here's an outline of the LZW Compression Algorithm:ππ 0. a. Initialize the hash table. (That means for each possibleπ character there is one "root" entry in the table. Someπ flavors of LZW may actually have a "null" non-characterπ at the base of the table.)π b. Initialize the "current" string (S) to null.ππ 1. Read a character (K) from the input stream.π (If there are none left, then we're done, of course.)ππ 2. Is the string S+K in the string-table?ππ 3. If yes, then a. S := S+Kπ b. Go to Step 1.ππ If no, then a. add S+K to the string table.π b. output the code for Sπ c. S := Kπ d. Go to Step 1.ππ Decompression is very similar.ππ 0. a. Initialize the string table (the same as with compression).π b. Read the first code from the compressed file into Cπ c. Then output the equivalent string for code Cπ d. Let code O := code C (The code, not the string!)ππ 1. Read the next code from the input stream into Cππ 2. Does code C exist in the string/hash table?ππ 3. If yes, then a. output the string for code Cπ b. S := equivalent string for code Oπ c. K := first character of the string for code Cπ d. add S+K to the string tableππ If no, then a. S := equivalent string for code Oπ b. K := first character of Sπ c. output S+Kπ d. add S+K to the tableππ 4. code O := code Cππ 5. Go to Step 1.ππ It may seem psychotic at first, but it works. Just keep reading it andπ thinking about it. The best way is with a pencil and pad experimentingπ with a character set of four characters (A, B, C, and D) and a "word"π like "ABACADABA" and following through the steps.ππ (An actual walk-through is not included here, since it will take upπ *way* too much space. I recommend getting further, more detailedπ descriptions of LZW if you plan on writing an implementation.)ππ One important thing is to *not* confuse between the `code' for a stringπ (it's index in the hash table) and the string itself.ππ Two points about LZW implementations:ππ 1. The code stream (i.e. the compressed output) is not usuallyπ a set number of bits, but reflects how many entries are inπ the string table. This is another way to further compressπ the data.ππ Usually, LZW schemes will output the code in the number of bitsπ exactly reflecting the string table. Thus for the first 512π codes the output is 9-bits. Once the 513th string is added toπ the table, it's 10-bits and so on.ππ Some implementations will use a constant output until a thresholdπ is reached. For example, the code output will always be 12-bitsπ until the 4097th string is added, then the code output is inπ 16-bit segments etc...ππ If these scenarios are used, the decompression routine *must*π "think ahead" so as to read in the proper number of bits fromπ the encoded data.ππ 2. Because full string tables are incredible memory hogs, manyπ flavors of LZW will use a "hash" table (see, string and hashπ table aren't quite the same).ππ Instead of a string of characters like "ABC", there'll beπ a table containing the last character of the string, and aπ reference to the previous character(s). Thus we'll haveπ "A", "[A]B" and "[[A]B]C" where [x] is the reference (or theπ actual "code") for that character/string. Using this methodπ may be slower, but it saves memory and makes the implement-π ation a little easier.π There are many variations on LZW using other sets of strings toπ compare with the string table--this is based on the assumption thatπ the more entries there are in the table, the more efficient theπ compression will be.ππ One variation (Lampev-Ziv-Yakoo) would add the ButFirst(S+K) everyπ time S+K was added. "ButFirst" means all characters but the firstπ one. So ButFirst("ABCD") = "BCD".πππAnyhow, those are the compression algorithms that I know about which Iπcan even remotely attempt to explain.πI apologize for any (glaring?) mistakes. It's late...what started as aπmeaningless reply to somebody asking something about SQUEEZE explodedπinto this post. I hope it's useful to anybody interested.π}π 22 11-26-9317:05ALL VARIOUS FAQ - Modems SWAG9311 375 ~O πThis is a FAQ answer on serial communications using the TTY protocol. Itπcontains information on the TTY protocol and hardware and software implemen-πtations on IBM PCs which is derived from National Semiconductor data sheets.ππPART ONE - HARDWARE & SOFTWAREπππAcknowledgementsπ================ππ The following persons have contributed (directly or indirectly :-) to thisπsummary:π Madis Kaal <mast@anubis.kbfi.ee ??> this address is known to be badπ Steve Poulsen <stevep@ims.com>π Scott C. Sadow <NS16550A@mycro.UUCP>π Dan Norstedt <?>π [Commercial: This line could display YOUR name!]ππππIntroductionπ============ππ One of the most universal parts of the PC is its serial port. You canπconnect a mouse, a modem, a printer, a plotter, another PC, ...π But its usage (both software and hardware) is one of the best-kept secretsπfor most users, besides that it is not difficult to understand how toπconnect (not plug-in) devices to it and how to program it.π Regard this FAQ as a manual of the serial port of your PC for bothπhardware and software.πππHistorical summaryπ------------------ππ In early days of telecommunications, errand-boys and optical signals (flags,πlights, clouds of smoke) were the only methods of transmitting informationπacross long distances. With increasing requirements on speed and growingπamount of information, more practical methods were developed. One milestoneπwas the first wire-bound transmission on May 24th, 1844 ("What hath Godπwrought", using the famous Morse-alphabet). Well, technology improved a bit,πand soon there were machines that could be used like typewriters, except thatπyou typed not only on your own piece of paper but also on somebody elses.πThe only thing that has changed on the step from the teletyper to your PCπis speed.πππThe TTY (teletyping) protocolπ-----------------------------ππ Definition: A protocol is a clear description of the LOGICAL method ofπtransmitting information. This does NOT include physical realisation.ππ The TTYp uses two different states of the line called 'mark' and 'space'.πIf no data is transmitted, the line is in the 'space' state. Data looksπlikeππ space ----------+ +-------+ +---+ +-------π | | | | | |π mark +---+ +---+ +---+ππ (1) --------(2)-------- (3)ππ (1) start bit (2) data bits (3) stop bit(s)ππ Both transmitter (TX) and receiver (RX) use the same data rate (measuredπin baud, which is the reciprocal value of the smallest time interval betweenπtwo changes of the line state. TX and RX know about the number of dataπbits (probably with a parity bit added), and both know about the size ofπthe stop step (called the stop bit or the stop bits, depending on the sizeπof the stop step; normally 1, 1.5 or 2 times the size of a data bit). Dataπis transmitted bit-synchroneously and word-asynchroneously, which means thatπthe size of the bits, the length of the word etc.pp. is clearly definedπbut the time between two words is undefined.π The start bit indicates the beginning of a new data word. It is used toπsynchronize transmitter and receiver.π Data is transmitted LSB to MSB, which means that the least significantπbit (Bit 0) is transmitted first with 4 to 7 bits of data following, re-πsulting in 5 to 8 bits of data. A logical '0' is transmitted by theπ'space' state of the line, a logical '1' by 'mark'.π A parity bit can be added to the data bits to allow error detection.πThere are two (well, actually five) kinds of parity: odd and even (plusπnone, mark and space). Odd parity means that the number of 'mark' steps inπthe data word (including parity bit) is always odd, so the parity bit isπset accordingly (I don't have to explain 'even' parity, must I?). It isπalso possible to set the parity bit to a fixed state or to omit it.π The stop bit does not indicate the end of the word (as it could beπderived from its name); it rather separates two consecutive words byπputting the line into the 'space' state for a minimum time.π The protocol is usually described by a sequence of numbers and letters,πe.g. 8n1 means 1 start bit (always), 8 bits of data, no parity bit, 1 stopπbit. 7e2 would indicate 7 bits of data, even parity, 2 stop bits (but I'veπnever seen this one...). The usual thing is 8n1 or 7e1.π Early teletypers used the neckbreaking speed of 50 baud (which meansπthat one step is 20ms), 5 bits of data, no parity and 1.5 stop bits (don'tπask me why!). Your PC is capable of serial transmission at up to 115,200πbaud (step size of 8.68 microseconds!). Typical rates are 300 baud, 1200 baud,π2400 baud and 9600 baud.πππThe physical transmissionπ-------------------------ππ Teletypers used a closed-loop line with a space current of 20ma and aπmark current of 0ma (typical), which allowed to detect a 'broken line'.πThe RS232C port of your PC uses voltages rather than currents to indicateπlogical states: 'space' is signaled by +3v to +15v (typically +12v), 'mark'πby -3v to -15v (typically -12V). The typical output impedance of the serialπport of a PC is 2 kiloohms (resulting in about 5ma @ 10v), the typical inputπimpedance is about 4.3 kiloohms, so there should be a maximum fan-out of 5π(5 inputs can be connected to 1 output). Please don't rely on this, it mayπdiffer from PC to PC.π Three lines (RX, TX & ground) are needed.ππQ. Why does my PC have a 25pin/9pin connector if there are only 3 linesπ needed?πA. There are several status lines that are only used with a modem. See theπ software section of this FAQ.ππQ. How can I easily connect two PCs by a three-wire lead?πA. This connection is called a 'null-modem' connection. RX1 is connectedπ to TX2 and vice versa, GND1 to GND2. In addition to this, connect RTSπ to CTS & DCD and DTR to DSR (modem software often relies on that). Seeπ the hardware section for further details.πππHardwareπ========πππThe connectorsπ--------------ππ PCs have 9pin/25pin male SUB-D connectors. The pin layout is as followsπ(looking at the back side of your PC):ππ 1 13 1 5π _______________________________ _______________π \ . . . . . . . . . . . . . / \ . . . . . /π \ . . . . . . . . . . . . / \ . . . . /π --------------------------- -----------π 14 25 6 9ππ Name (V24) 25pin 9pin Dir Full name Remarksπ--------------------------------------------------------------------------π TxD 2 2 o Transmit Dataπ RxD 3 3 i Receive Dataπ RTS 4 5 o Request To Sendπ CTS 5 8 i Clear To Sendπ DTR 20 4 o Data Terminal Readyπ DSR 6 6 i Data Set Readyπ RI 22 9 i Ring Indicatorπ DCD 8 1 i Data Carrier Detectπ GND 7 5 - Signal groundπ - 1 - - Protective ground Don't use this one!π SCTE 24 - - Sync. clock trans. end Several PCs onlyπ SCT 15 - o Sync. clock TX dito.π SCR 17 - i Sync. clock RX dito.ππ The most important lines are RxD, TxD, and GND. Others are used withπmodems, printers and plotters to indicate internal states or to useπsynchroneous transmission (this is rarely used, and most PCs don't supportπit).π '0' means +3v to +15V, '1' means -3v to -15v. '1' is the active state.ππ The lines are:ππ RxD, TxD: These lines carry the data.π RTS, CTS: Are used by the PC and the modem/printer/whatsoever (furtherπ on referred to as the data set) to start/stop a communication. The PCπ sets RTS to its active state ('1'), and the data set responds with CTSπ '1' (always in this order). If the data set wants to stop/interrupt theπ communication (e.g. buffer overflow), it drops CTS to '0'; the PC usesπ RTS to control the data flow.π DTR, DSR: Are used to establish a connection at the very beginning, i.e.π the PC and the data set 'shake hands' first to assure they are bothπ present. The PC sets DTR to '1', and the data set answers with DSRπ '1'. Modems often indicate hang-up by resetting DSR to '0'.π (These six lines plus GND are often referred to as '7 wire'-connection orπ 'hand shake'-connection.)π DCD: The modem uses this line to indicate that it has detected theπ carrier of the modem on the other side of the line.π RI: The modem uses this line to signal that 'the phone rings' (even ifπ there isn't a bell fitted to your modem).π SCTE, SCT, SCR: forget about these.π Protective ground: This line is connected to the power ground of theπ serial adapter. It should not be used as a signal ground, and itπ MUST NOT be connected to GND (even if your DMM shows up aπ connection!). Connect this line to the screen of the lead (if there isπ one).ππ Technical data (typical):ππ Signal level: -10.5v/+11vπ Short circuit current: 6.8ma (yes, that's enough for your mouse!)π Output impedance: 2 kiloohmsπ Input impedance: 4.3 kiloohmsπππConnecting devicesπ------------------ππ Normally, a 7 wire connection is used. Connect:π GND1 to GND2π RxD1 to TxD2π TxD1 to RxD2π DTR1 to DSR2π DSR1 to DTR2π RTS1 to CTS2π CTS1 to RTS2π If a modem is connected, add lines for the following:π RI, DCDπ If software wants it, connect DCD1 to CTS1 and DCD2 to CTS2.π BEWARE! While PCs use pin 2 for RxD and pin 3 for TxD, modems normallyπhave those pins reversed! This allows to easily connect pin1 to pin1, pin2πto pin 2 etc. If you connect two PCs, cross RxD and TxD.ππ If hardware handshaking is not needed, a so-called null-modem connectionπcan be used. Connect:π GND1 to GND2π RxD1 to TxD2π TxD1 to RxD2πAdditionally, connect (if software needs it):π RTS1 to CTS1 & DCD1π RTS2 to CTS2 & DCD2π DTR1 to DSR1π DTR2 to DSR2πYou won't need long wires for these!π The null-modem connection is used to establish an XON/XOFF-transmissionπbetween two PCs (see software section for details).π Remember: the names DTR, DSR, CTS & RTS refer to the lines as seen fromπthe PC. This means that for your data set DTR & RTS are incoming signalsπand DSR & CTS are outputs!πππBase addresses & interruptsπ---------------------------ππ Normally, the following list is correct:πππ Port Base address Int #ππ COM1 0x3F8 0xCπ COM2 0x2F8 0xBπ COM3 0x3E8 0xCπ COM4 0x2E8 0xBπππ In PCs, serial communication is realized with a set of three chipsπ(there are no further components needed!): a UART (Universal AsynchroneousπReceiver/Transmitter) and two line drivers. Normally, the 82450/16450/8250πdoes the 'brain work' while the 1488 and 1489 drive the lines.π The chips are produced by many manufacturers; it's of no importanceπwhich letters are printed in front of the numbers (mostly NS for NationalπSemiconductor). Don't regard the letters behind the number also; they justπindicate special features and packaging (Advanced, FIFO, New, MILitary,πbug fixes [see below] etc.).π You might have heard of the possibility to replace the 16450 by a 16550Aπto improve reliability and software throughput. This is only useful if yourπsoftware is able to use the FIFO (first in-first out) buffer features. Theπchips are fully pin-compatible except for two pins that are not used byπany board known to the author: pin 24 (CSOUT, chip select out) and pin 29π(NC, no connection to be made). With the 16550A, pin 24 is -TXRDY and pinπ29 is -RXRDY, signals that aren't needed and that even won't care if theyπare shorted to +5v or ground. Therefore it should always be possible toπsimply replace the 16450 by the 16550A - even if it's not always useful dueπto lacking software capabilities. IT IS DEFINITELY NOT NECESSARY FORπCOMMUNICATION UP TO LOUSY 9600 BAUD! These rates can easily be handled byπany CPU and the interrupt-driven communication won't slow down the computerπsubstantially. But if you want to use high-speed transfer with or withoutπusing the interrupt features (i.e. by 'polling'), it is recommended to useπthe 16550A in order to make transmission more reliable if your softwareπsupports it (see excursion some pages below).πππHow to detect which chip is usedπ--------------------------------ππ This is really not difficult. The 8250 has no scratch register (see dataπsheet info below), the 16450/82450 has no FIFO, the 16550 has no workingπFIFO :-) and the 16550A performs alright. See the software section forπan example.πππData sheet informationπ----------------------ππ Some hardware information taken from the data sheet of NationalπSemiconductor (shortened and commented):ππ Pin description of the 16450(16550A) [Dual-In-Line package]:ππ +-----+ +-----+π D0 -| 1 +-+ 40|- VCCπ D1 -| 2 39|- -RIπ D2 -| 3 38|- -DCDπ D3 -| 4 37|- -DSRπ D4 -| 5 36|- -CTSπ D5 -| 6 35|- MRπ D6 -| 7 34|- -OUT1π D7 -| 8 33|- -DTRπ RCLK -| 9 32|- -RTSπ SIN -| 10 31|- -OUT2π SOUT -| 11 30|- INTRπ CS0 -| 12 29|- NC (-RXRDY)π CS1 -| 13 28|- A0π -CS2 -| 14 27|- A1π -BAUDOUT -| 15 26|- A2π XIN -| 16 25|- -ADSπ XOUT -| 17 24|- CSOUT (-TXRDY)π -WR -| 18 23|- DDISπ WR -| 19 22|- RDπ VSS -| 20 21|- -RDπ +-------------+ππA0, A1, A2, Register Select, Pins 26-28:πAddress signals connected to these 3 inputs select a UART register forπthe CPU to read from or to write to during data transfer. A table ofπregisters and their addresses is shown below. Note that the state of theπDivisor Latch Access Bit (DLAB), which is the most significant bit of theπLine Control Register, affects the selection of certain UART registers.πThe DLAB must be set high by the system software to access the BaudπGenerator Divisor Latches.ππ DLAB A2 A1 A0 Registerπ 0 0 0 0 Receive Buffer (read) Transmitter Holding Reg. (write)π 0 0 0 1 Interrupt Enableπ x 0 1 0 Interrupt Identification (read)π x 0 1 0 FIFO Control (write) (undefined on the 16450. CB)π x 0 1 1 Line Controlπ x 1 0 0 Modem Controlπ x 1 0 1 Line Statusπ x 1 1 0 Modem Statusπ x 1 1 1 Scratch (special use on some boards. CB)π 1 0 0 0 Divisor Latch (LSB)π 1 0 0 1 Divisor Latch (MSB)ππ-ADS, Address Strobe, Pin 25: The positive edge of an active AddressπStrobe (-ADS) signal latches the Register Select (A0, A1, A2) and ChipπSelect (CS0, CS1, -CS2) signals.πNote: An active -ADS input is required when Register Select and ChipπSelect signals are not stable for the duration of a read or writeπoperation. If not required, tie the -ADS input permanently low. (As it isπdone in your PC. CB)ππ-BAUDOUT, Baud Out, Pin 15: This is the 16 x clock signal from theπtransmitter section of the UART. The clock rate is equal to the mainπreference oscillator frequency divided by the specified divisor in theπBaud Generator Divisor Latches. The -BAUDOUT may also be used for theπreceiver section by tying this output to the RCLK input of the chip. (Yep,πthat's true for your PC. CB).ππCS0, CS1, -CS2, Chip Select, Pins 12-14: When CS0 and CS1 are high and CS2πis low, the chip is selected. This enables communication between the UARTπand the CPU.ππ-CTS, Clear To Send, Pin 36: When low, this indicates that the modem orπdata set is ready to exchange data. This signal can be tested by readingπbit 4 of the MSR. Bit 4 is the complement of this signal, and Bit 0 is '1'πif -CTS has changed state since the previous reading (bit0=1 generates anπinterrupt if the modem status interrupt has been enabled).ππD0-D7, Data Bus, Pins 1-8: Connected to the data bus of the CPU.ππ-DCD, Data Carrier Detect, Pin 38: blah blah blah, can be tested byπreading bit 7 / bit 3 of the MSR. Same text as -CTS.ππDDIS, Driver Disable, Pin 23: This goes low whenever the CPU is readingπdata from the UART.ππ-DSR, Data Set Ready, Pin 37: blah, blah, blah, bit 5 / bit 1 of MSR.ππ-DTR, Data Terminal Ready, Pin 33: can be set active low by programmingπbit 0 of the MCR '1'. Loop mode operation holds this signal in itsπinactive state.ππINTR, Interrupt, Pin 30: goes high when an interrupt is requested by theπUART. Reset low by the MR.ππMR, Master Reset, Pin 35: Schmitt Trigger input, resets internal registersπto their initial values (see below).ππ-OUT1, Out 1, Pin 34: user-designated output, can be set low byπprogramming bit 2 of the MCR '1' and vice versa. Loop mode operation holdsπthis signal inactive high.ππ-OUT2, Out 2, Pin 31: blah blah blah, bit 3. (Used in your PC to connectπthe UART to the interrupt line of the slot when '1'. CB)ππRCLK, Receiver Clock, Pin 9: This input is the 16 x baud rate clock forπthe receiver section of the chip. (Normally connected to -BAUDOUT, as inπyour PC. CB)ππRD, -RD, Read, Pins 22 and 21: When Rd is high *or* -RD is low while theπchip is selected, the CPU can read data from the UART. (One of these isπnormally tied. CB)ππ-RI, Ring Indicator, Pin 39: blah blah blah, Bit 6 / Bit 2 of the MSR.π(Bit 2 indicates only change from active low to inactive high! Curious,πisn't it? CB)ππ-RTS, Request To Send, Pin 32: blah blah blah, see DTR (Bit 1).ππSIN, Serial Input, Pin 10.ππSOUT, Serial Output, Pin 11: ... Set to 'space' (high) upon MR.ππ-RXRDY, -TYRDY: refer to NS data sheet. Those pins are used for DMAπchanneling. Since they are not connected in your PC, I won't describe themπhere.ππVCC, Pin 40, +5vππVSS, Pin 20, GNDππWR, -WR: same as Rd, -RD for writing data.ππXIN, XOUT, Pins 16 and 17: Connect a crystal here (1.5k betw. xtal & pin 17)πand pin 16 with a capacitor of approx. 20p to GND and other xtal conn. 40pπto GND. Resistor of approx. 1meg parallel to xtal. Or use pin 16 as an inputπand pin 17 as an output for an external clock signal.πππAbsolute Maximum Ratings:ππ Temperature under bias: 0 C to +70 Cπ Storage Temperature: -65 C to 150 Cπ All input or output voltages with respect to VSS: -0.5v to +7.0vπ Power dissipation: 1WππFurther electrical characteristics see the very good data sheet of NS.ππUART Reset ConfigurationππRegister/Signal Reset Control Reset Stateπ--------------------------------------------------------------------π IER MR 0000 0000π IIR MR 0000 0001π FCR MR 0000 0000π LCR MR 0000 0000π MCR MR 0000 0000π LSR MR 0110 0000π MSR MR xxxx 0000 (according to signals)π SOUT MR highπ INTR (RCVR errs) Read LSR/MR lowπ INTR (data ready) Read RBR/MR lowπ INTR (THRE) Rd IIR/Wr THR/MR lowπ INTR (modem status) Read MSR/MR lowπ -OUT2 MR highπ -RTS MR highπ -DTR MR highπ -OUt1 MR highπ RCVR FIFO MR/FCR1&FCR0/DFCR0 all bits lowπ XMIT FIFO MR/FCR1&FCR0/DFCR0 all bits lowππππKnown problems with several chipsπ---------------------------------ππ(From material Madis Kaal received from Dan Norstedt)ππ 8250 and 8250-B:ππ * These UARTs pulse the INT line after each interrupt cause hasπ been serviced (which none of the others do). [Generates interruptπ overhead. CB]ππ * The start bit is about 1 us longer than it ought to be. [Thisπ shouldn't be a problem. CB]ππ * 5 data bits and 1.5 stop bits doesn't work.ππ * When a 1 bit is written to the bit 1 (Tx int enab) in the IER,π a Tx interrupt is generated. This is an erroneous interruptπ if the THRE bit is not set. [So don't set this bit as long asπ the THRE bit isn't set. CB]ππ * The first valid Tx interrupt after the Tx interrupt is enabledπ is probably missed. Suggested workaround:π 1) Wait for the TRHE bit to become set.π 2) Disable CPU interrupts.π 3) Write Tx interrupt enable to the IER.π 4) Write Tx interrupt enable to the IER, again.π 5) Enable CPU interrupts.ππ * The TSRE (bit 6) doesn't work properly.ππ * If both the Rx and Tx interrupts are enabled, and a Rx interruptπ occurs, the IIR indication may be lost; Suggested workarounds:π 1) Test THRE bit in the Rx routine, and either set IER bit 1π or call the Tx routine directly if it is set.π 2) Test the THRE bit instead of using the IIR.ππ * [If one of these chips vegetates in your PC, go get your solderπ iron heated... CB]ππ 8250A, 82C50A, 16450 and 16C450:ππ * (Same problem as above:)π If both the Rx and Tx interrupts are enabled, and a Rx interruptπ occurs, the IIR indication may be lost; Suggested workarounds:π 1) Test THRE bit in the Rx routine, and either set IER bit 1π or call the Tx routine directly if it is set.π 2) Test the THRE bit instead of using the IIR.π 3) [Don't enable both interrupts at the same time. I've neverπ had any need to do this. CB]ππ 16550 (without the A):ππ * Rx FIFO bug: Sometimes a FIFO will get extra characters.π [This seemed to be very embarracing for NS; they've added aπ simple detection method in the 16550A (bit 6 of IIR). CB]ππNo bugs reported in the 16550A (yet?)ππ[Same is true for the 16C552, a two-in-one version of the 16550A. CB]ππππSoftwareπ========πππ First some information from the data sheet. Then: HOW TO USE IT.πππRegister Descriptionπ--------------------ππSee "Hardware" for addresses.ππRegister Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7ππRBR (r/o) ----------------------- data bits received ------------------------πTHR (w/o) ------------------ data bits to be transmitted --------------------πIER ERBFI ETBEI ELSI EDSSI 0 0 0 0πIIR (r/o) pending IID0 IID1 IID2 0 0 FIFO en FIFOenπFCR (w/o) enable RFres XFres DMAsel 0 0 - RX trigger -πLCR - word length - stopbits PAR en even sel stick par SBR DLABπMCR DTR RTS OUT1 OUT2 Loop 0 0 0πLSR RBF OE PE FE Break THRE TEMT FIFOerrπMSR DCTS DDSR TERI DDCD CTS DSR RI DCDππERBFI: Enable Receiver Buffer Full InterruptπETBEI: Enable Transmitter Buffer Empty InterruptπELSI: Enable Line Status InterruptπEDSSI: Enable Delta Status Signals InterruptπIID#: Interrupt IDentificationπRFres: Receiver FIFO resetπXFres: Transmitter FIFO resetπSBR: Set BReakπRBF: Receiver Buffer Full (Data Available)πOE: Overrun ErrorπPE: Parity ErrorπFE: Framing ErrorπTHRE: Transmitter Holding Register Empty (new data can be written to THR)πTEMT: Transmitter Empty (last word has been sent)πDCTS: Delta Clear To SendπDDSR: Delta Data Set ReadyπTERI: Trailing Edge Ring IndicatorπDDCD: Delta Data Carrier DetectππLCR (Line Control Register):ππ Bit 1 Bit 0 word length Bit 2 Stop bitsπ 0 0 5 bits 0 1π 0 1 6 bits 1 1.5/2π 1 0 7 bits (1.5 if word length is 5)π 1 1 8 bits (1.5 does not work with some chips, see above)ππ Bit 5 Bit 4 Bit 3 Parity type Bit 6 SOUT conditionπ x x 0 no parity 0 normal operationπ 0 0 1 odd parity 1 force 'mark' (break)π 0 1 1 even parity Bit 7 DLABπ 1 0 1 mark parity 0 normal registersπ 1 1 1 space parity 1 divisor at reg 0, 1ππBaud Rate Generator:ππ DLAB must be set. Write word (16 bits) to address 0 of the UART (this isπthe base address) to program baud rate as follows:π xtal frequency in Hz / 16 / rate = divisorπ Your PC uses an xtal frequency of 1.8432 MHz.π Do *NOT* use 0 as a divisor (your maths teacher told you so)! [Itπresults in a rate of some 1000 baud. CB]π An error of up to 5 percent is irrelevant.π Some values:ππ Baud rate Divisor (hex) Percent Errorπ 50 900 0.0%π 75 600 0.0%π 110 417 0.026%π 134.5 359 0.058%π 150 300 0.0%π 300 180 0.0%π 600 C0 0.0%π 1200 60 0.0%π 1800 40 0.0%π 2000 3A 0.69%π 2400 30 0.0%π 3600 20 0.0%π 4800 18 0.0%π 7200 10 0.0%π 9600 C 0.0%π 19200 6 0.0%π 38400 3 0.0%π 56000 2 2.86%π 115200 1 0.0%ππ NS specifies that the 16550A is capable of 256 kbaud if you use a 4 MHzπor an 8 MHz crystal. But a staff member of NS Germany (I know that thisπabbreviation is not well-chosen :-( ) told one of my friends on the phoneπthat it runs correctly at 512 kbaud as well, but I don't know if theπ1488/1489 manage this. This is true for the 16C552, too.π BTW: Ever tried 1.76 baud? Kindergarten kids write faster.π Mice typically use 2400 baud, 8n1.πππLSR (Line Status Register):ππ Bit 0 Data Ready (DR). Reset by reading RBR.π Bit 1 Overrun Error (OE). Reset by reading LSR. Indicates loss of data.π Bit 2 Parity Error (PE). Indicates transmission error. Reset by LSR.π Bit 3 Framing Error (FE). Indicates missing stop bit. Reset by LSR.π Bit 4 Break Indicator (BI). Set if 'space' for more than 1 word. Resetπ by LSR.π Bit 5 Transmitter Holding Register Empty (THRE). Indicates that a newπ word can be written to THR. Reset by writing THR.π Bit 6 Transmitter Empty (TEMT). Indicates that no transmission isπ running. Reset by reading LSR.π Bit 7 Set if at least 1 word in FIFO has been received with an error.π Cleared by reading LSR if there is no further error in the FIFO.ππFCR (FIFO Control Register):ππ Bit 0: FIFO enable.π Bit 1: Clear receiver FIFO. This bit is self-clearing.π Bit 2: Clear transmitter FIFO. This bit is self-clearing.π Bit 3: DMA mode (pins -RXRDY and -TXRDY), see sheetπ Bits 6-7:Trigger level of the DR-interrupt.π Bit 7 Bit 6 Receiver FIFO trigger levelπ 0 0 01π 0 1 04π 1 0 08π 1 1 14πππ Excursion: why and how to use the FIFOs (by Scott C. Sadow)π -----------------------------------------------------------ππ Normally when transmitting or receiving, the UART generates anπ interrupt for every character sent or received. For 2400 baud, typicallyπ this is 240/second. For 115,200 baud, this means 11,520/second. With FIFOsπ enabled, the number of interrupts is greatly reduced. For transmitπ interrupts, the UART indicates the transmit holding register is not busyπ until the 16 byte FIFO is full. A transmit hold register empty interruptπ is not generated until the FIFO is empty (last byte is being sent) Thus,π the number of transmit interrupts is reduced by a factor of 16. Forπ 115,200 baud, this means only 7,200 interrupts/second. For receive dataπ interrupts, the processing is similar to transmit interrupts. The mainπ difference is that the number of bytes in the FIFO before generating anπ interrupt can be set. When the trigger level is reached, a recieve dataπ interrupt is generated, but any other data received is put in the FIFO.π The receive data interrupt is not cleared until the number of bytes in theπ FIFO is below the trigger level.ππ To add 16550A support to existing code, there are 2 requirements.ππ 1) When reading the IIR to determine the interrupt source, onlyπ use the lower 3 bits.ππ 2) After the existing UART initialization code, try to enable theπ FIFOs by writing to the FCR. (A value of C7 hex will enable FIFO mode,π clear both FIFOs, and set the receive trigger level at 14 bytes) Next,π read the IIR. If Bit 6 of the IIR is not set, the UART is not aπ 16550A, so write 0 to the FCR to disable FIFO mode.πππIIR (Interrupt Identification Register):ππ Bit 3 Bit 2 Bit 1 Bit 0 Priority Source Descriptionπ 0 0 0 1 noneπ 0 1 1 0 highest Status OE, PE, FE or BI of theπ LSR set. Serviced byπ reading the LSR.π 0 1 0 0 second Receiver DR or trigger level rea-π ched. Serviced by read-π ing RBR 'til under levelπ 1 1 0 0 second FIFO No Receiver FIFO actionπ since 4 words' timeπ (neither in nor out) butπ data in RX-FIFO. Servicedπ by reading RBR.π 0 0 1 0 third Transm. THRE. Serviced by read-π ing IIR (if source ofπ int only!!) or writingπ to THR.π 0 0 0 0 fourth Modem One of the delta flagsπ in the MSR set. Servicedπ by reading MSR.π Bit 6 & 7: 16550A: set if FCR bit 0 set.π 16550: bit 7 set, bit 6 clearedπ others: clearπ In most software applications bits 3, 6 & 7 should be masked when servicingπ the interrupt since they are not relevant. These bits cause trouble withπ old software relying on that they are cleared...π NOTE! Even if some of these interrupts are masked, the service routineπ can be confronted with *all* states shown above when the IIR is loop-polledπ until bit 0 is set. Check examples.ππIER (Interrupt Enable Register):ππ Bit 0: If set, DR interrupt is enabled.π Bit 1: If set, THRE interrupt is enabled.π Bit 2: If set, Status interrupt is enabled.π Bit 3: If set, Modem status interrupt is enabled.ππMCR (Modem Control Register):ππ Bit 0: Programs -DTR. If set, -DTR is low and the DTR pin of the port isπ '1'.π Bit 1: Programs -RTS.π Bit 2: Programs -OUT1. Not used in a PC.π Bit 3: Programs -OUT2. If set, interrupts generated by the UART are trans-π ferred to the ICU (Interrupt Control Unit).π Bit 4: '1': local loopback. All outputs disabled.ππMSR (Modem Status Register):ππ Bit 0: Delta CTS. Set if CTS has changed state since last reading.π Bit 1: Delta DSR. Set if DSR has changed state since last reading.π Bit 2: TERI. Set if -RI has changed from low to high (i.e. RI at portπ has changed from '1' to '0').π Bit 3: Delta DCD. Set if DCD has changed state since last reading.π Bit 4: CTS. 1 if '1' at port.π Bit 5: DSR.π Bit 6: RI. If loopback is selected, it is equivalent to OUT1.π Bit 7: DCD.πππPART TWO - PROGRAMMINGπππProgrammingπ-----------ππ Now for the clickety-clickety thing. I hope you're a bit keen inπassembler programming (if not, you've got a problem B-). Programming the UARTπin high level languages is, of course, possible, but not at very highπrates or interrupt-driven. I give you several routines in assembler (and,πwherever possible, in C) that do the dirty work for you.ππ First thing to do is detect which chip is used. It shouldn't be difficultπto convert this C function into assembler; I'll omit the assembly version.ππint detect_UART(unsigned baseaddr)π{π // this function returns 0 if no UART is installed.π // 1: 8250, 2: 16450, 3: 16550, 4: 16550Aπ int x;π // first step: see if the LCR is thereπ outp(baseaddr+3,0x1b);π if (inp(baseaddr+3)!=0x1b) return 0;π outp(baseaddr+3,0x3);π if (inp(baseaddr+3)!=0x3) return 0;π // next thing to do is look for the scratch registerπ outp(baseaddr+7,0x55);π if (inp(baseaddr+7)!=0x55) return 1;π outp(baseaddr+7,0xAA);π if (inp(baseaddr+7)!=0xAA) return 1;π // then check if there's a FIFOπ outp(baseaddr+2,0x1);π x=inp(baseaddr+2);π if ((x&0x80)==0) return 2;π if ((x&0x40)==0) return 3;π return 4;π}ππ Remember: if it's not a 16550A, don't use the FIFO mode!πππ Now the non-interrupt version of TX and RX.ππ Let's assume the following constants are set correctly (either byπ'CONSTANT EQU value' or by '#define CONSTANT value'). You can easily useπvariables instead, but I wanted to save the extra lines for the ADDπcommands necessary then...ππ UART_BASEADDR the base address of the UARTπ UART_BAUDRATE the divisor value (e.g. 12 for 9600 baud)π UART_LCRVAL the value to be written to the LCR (e.g. 0x1b for 8N1)π UART_FCRVAL the value to be written to the FCR. Bit 0, 1 and 2 set,π bits 6 & 7 according to trigger level wished (see above).π 0x87 is a good value.ππ First thing to do is initializing the UART. This works as follows:ππinit_UART proc nearπ push ax ; we are 'clean guys'π push dxπ mov dx,UART_BASEADDR+3 ; LCRπ mov al,80h ; set DLABπ out dx,alπ mov dx,UART_BASEADDR ; divisorπ mov ax,UART_BAUDRATEπ out dx,axπ mov dx,UART_BASEADDR+3 ; LCRπ mov al,UART_LCRVAL ; paramsπ out dx,alπ mov dx,UART_BASEADDR+4 ; MCRπ xor ax,ax ; clear loopbackπ out dx,alπ ;***π pop dxπ pop axπ retπinit_UART endpππvoid init_UART()π{π outp(UART_BASEADDR+3,0x80);π outpw(UART_BASEADDR,UART_BAUDRATE);π outp(UART_BASEADDR+3,UART_LCRVAL);π outp(UART_BASEADDR+4,0);π //***π}ππ If we wanted to use the FIFO functions of the 16550A, we'd have to addπsome lines to the routines above (where the ***s are).πIn assembler:π mov dx,UART_BASEADDR+2 ; FCRπ mov al,UART_FCRVALπ out dx,alπAnd in C:π outp(UART_BASEADDR+2,UART_FCRVAL);ππ Don't forget to disable the FIFO when your program exits! Some otherπsoftware may rely on this!ππ Not very complex so far, isn't it? Well, I told you so at the veryπbeginning, and we wanted to start easy. Now let's send a character.ππUART_send proc nearπ ; character to be sent in ALπ push dxπ push axπ mov dx,UART_BASEADDR+5πus_wait:π in al,dx ; wait until we are allowed to write a byte to the THRπ test al,20hπ jz us_waitπ pop axπ mov dx,UART_BASEADDRπ out dx,al ; then write the byteπ pop axπ pop dxπ retπUART_send endpππvoid UART_send(char character)π{π while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}π outp(UART_BASEADDR,(int)character);π}ππ This one sends a null-terminated string.ππUART_send_string proc nearπ ; DS:SI contains a pointer to the string to be sent.π push siπ push axπ push dxπ cld ; we want to read the string in its correct orderπuss_loop:π lodsbπ or al,al ; last character sent?π jz uss_endeπ ;*1*π mov dx,UART_BASEADDR+5π push axπuss_wait:π in al,dxπ test al,20hπ jz uss_waitπ mov dx,UART_BASEADDRπ pop axπ out dx,alπ ;*2*π jmp uss_loopπuss_ende:π pop dxπ pop axπ pop siπ retπUART_send_string endpππvoid UART_send_string(char *string)π{π int i;π for (i=0; string[i]!=0; i++)π {π //*1*π while ((inp(UART_BASEADDR+5)&0x20)!=0) {;}π outp(UART_BASEADDR,(int)string[i]);π //*2*π }π}ππ Of course, we could have used our already programmed function/procedureπUART_send instead of the piece of code limited by *1* and *2*, but we areπinterested in high-speed code.ππ It shouldn't be a hard nut for you to modify the above function/procedureπso that it sends a block of data rather than a null-terminated string. I'llπomit that here.ππ Now for reception. We want to program routines that do the following:π - check if a character received or an error occuredπ - read a character if there's one availableππ Both the C and the assembler routines return 0 (in AX) if there isπneither an error condition nor a character available. If a character isπavailable, Bit 8 is set and AL or the lower byte of the return valueπcontains the character. Bit 9 is set if we lost data (overrun), bit 10πsignals a parity error, bit 11 signals a framing error, bit 12 shows ifπthere is a break in the data stream and bit 15 signals if there are anyπerrors in the FIFO (if we turned it on). The procedure/function is muchπsmaller than this paragraph:ππUART_get_char proc nearπ push dxπ mov dx,UART_BASEADDR+5π in al,dxπ mov ah,alπ and ah,9fhπ test al,1π jz ugc_nocharπ mov dx,UART_BASEADDRπ in al,dxπugc_nochar:π pop dxπ retπUART_get_char endpππunsigned UART_get_char()π{π unsigned x;π x=(inp(UART_BASEADDR+5)<<8)&0x9f;π if (x&0x100) x|=((unsigned)inp(UART_BASEADDR))&0xff);π return x;π}ππ This procedure/function lets us easily keep track of what's happeningπwith the RxD pin. It does not provide any information on the modem statusπlines! We'll program that later on.ππ If we wanted to show what's happening with the RxD pin, we'd just have toπwrite a routine like the following (I use a macro in the assembler versionπto shorten the source code):ππDOS_print macro pointerπ ; prints a string in the code segmentπ push axπ push dsπ push csπ pop dsπ mov dx,pointerπ mov ah,9π int 21hπ pop dsπ pop axπ endmππUART_watch_rxd proc nearπuwr_loop:π ; check if keyboard hit; we want a possibility to break the loopπ mov ah,1π int 16hπ jnz uwr_exitπ call UART_get_charπ or ax,axπ jz uwr_loopπ test ah,1 ; is there a character in AL?π jz uwr_nodataπ push ax ; yes, print itπ mov dl,alπ mov ah,2π int 21hπ pop axπuwr_nodata:π test ah,0eh ; any error at all?π jz uwr_loop ; this speeds up thingsπ test ah,2 ; overrun error?π jz uwr_nooverπ DOS_print overrun_textπuwr_noover:π test ah,4 ; parity error?π jz uwr_noparπ DOS_print parity_textπuwr_nopar:π test ah,8 ; framing error?π jz uwr_loopπ DOS_print framing_textπ jmp uwr_loopπoverrun_text db "*** Overrun Error ***$"πparity_text db "*** Parity Error ***$"πframing_text db "*** Framing Error ***$"πUART_watch_rxd endpππvoid UART_watch_rxd()π{π union _useful_π {π unsigned val;π char character;π } x;π while (!kbhit())π {π x.val=UART_get_char();π if (!x.val) continue; // nothing? Continueπ if (x.val&0x100) putc(x.character); // character? Print itπ if (!(x.val&0x0e00)) continue; // any error condidion? No, continueπ if (x.val&0x200) printf("*** Overrun Error ***");π if (x.val&0x400) printf("*** Parity Error ***");π if (x.val&0x800) printf("*** Framing Error ***");π }π}ππ If you call these routines from a function/procedure as shown below,πyou've got a small terminal program!ππterminal proc nearπter_loop:π call UART_watch_rxd ; watch line until a key is pressedπ xor ax,ax ; get that key from the bufferπ int 16hπ cmp al,27 ; is it ESC?π jz ter_end ; yes, then end this functionπ call UART_send ; send the character typed if it's not ESCπ jmp ter_loop ; don't forget to check if data comes inπter_end:π retπterminal endpππvoid terminal()π{π int key;π while (1)π {π UART_watch_rxd();π key=getche();π if (key==27) break;π UART_send((char)key);π }π}ππ These, of course, should be called from an embedding routine like theπfollowing (the assembler routines concatenated will assemble as an .EXEπfile. Put the lines 'code segment' and 'assume cs:code,ss:stack' to theπfront).ππmain proc nearπ call UART_initπ call terminalπ mov ax,4c00hπ int 21hπmain endpπcode endsπstack segment stack 'stack'π dw 128 dup (?)πstack endsπend mainππvoid main()π{π UART_init();π terminal();π}ππ Here we are. Now you've got everything you need to program null-modemπpolling UART software.π You know the way. Now go and add functions to check if a data set isπthere, then establish a connection. Don't know how? Set DTR, wait for DSR.πIf you want to send, set RTS and wait for CTS before you actually transmitπdata. You don't need to store old values of the MCR: this register isπreadable. Just read in the data, AND/OR the bit required and write theπbyte back.πππ Now for the interrupt-driven version of the program. This is going to beπa bit voluminous, so I draw the scene and leave the painting to you. If youπwant to implement interrupt-driven routines in a C program use either theπinline-assembler feature or link the objects together.ππ First thing to do is initialize the UART the same way as shown above.πBut there is some more work to be done before you enable the UARTπinterrupt: FIRST SET THE INTERRUPT VECTOR CORRECTLY! Use Function 0x25 ofπthe DOS interrupt 0x21. See also the note on known bugs if you've got aπ8250.ππUART_INT EQU 0Ch ; for COM2 / COM4 use 0bhπUART_ONMASK EQU 11101111b ; for COM2 / COM4 use 11110111bπUART_OFFMASK EQU 00010000b ; for COM2 / COM4 use 00001000bπUART_IERVAL EQU ? ; replace ? by any value between 0h and 0fhπ ; (dependent on which ints you want)π ; DON'T SET bit 1 yet!ππinitialize_UART_interrupt proc nearπ push dsπ push cs ; build a pointer in DS:DXπ pop dsπ lea dx,interrupt_service_routineπ mov ax,2500h+UART_INTπ int 21hπ pop dsπ mov dx,UART_BASEADDR+4 ; MCRπ in al,dxπ or al,8 ; set OUT2 bit to enable interruptsπ out dx,alπ mov dx,UART_BASEADDR+1 ; IERπ mov al,UART_IERVALπ out dx,alπ in al,21h ; last thing to do is unmask the int in the ICUπ and al,UART_ONMASKπ out 21h,alπ sti ; and free interrupts if they have been disabledπ retπinitialize_UART_interrupt endpππ Now the interrupt service routine. It has to follow several rules:πfirst, it MUST NOT change the contents of any register of the CPU! Then itπhas to tell the ICU (did I tell you that this is the interrupt controlπunit?) that the interrupt is being serviced. Next thing is test which partπof the UART needs service. Let's have a look at the following procedure:ππinterupt_service_routine proc far ; define as near if you want to link .COMπ ;*1*π push axπ push cxπ push dxπ push bxπ push spπ push bpπ push siπ push diπ ;*2* replace the part between *1* and *2* by pusha on an 80186+ systemπ push dsπ push esπ mov al,20h ; remember: first thing to do in interrupt routines is tellπ out 20h,al ; the ICU about it. This avoids lock-upπint_loop:π mov dx,UART_BASEADDR+2 ; IIRπ xor ax,ax ; clear AH; this is the fastest and shortest possibilityπ in al,dx ; check IIR infoπ test al,1π jnz int_endπ and al,6 ; we're interested in bit 1 & 2 (see data sheet info)π mov si,ax ; this is already an index! Well-devised, huh?π call word ptr cs:int_servicetab[si] ; ensure a near call is used...π jmp int_loopπint_end:π pop esπ pop dsπ ;*3*π pop diπ pop siπ pop bpπ pop spπ pop bxπ pop dxπ pop cxπ pop axπ ;*4* *3* - *4* can be replaced by popa on an 80186+ based systemπ iretπinterupt_service_routine endpππ This is the part of the service routine that does the decisions. Now weπneed four different service routines to cover all four interrupt sourceπpossibilities (EVEN IF WE DIDN'T ENABLE THEM!! Since 'unexpected'πinterrupts can have higher priority than 'expected' ones, they can appearπif an expected [not masked] interrupt situation shows up).ππint_servicetab DW int_modem, int_tx, int_rx, int_statusππint_modem proc nearπ mov dx,UART_BASE+6 ; MSRπ in al,dxπ ; do with the info what you like; probably just ignore it...π ; but YOU MUST READ THE MSR or you'll lock up the system!π retπint_modem endpππint_tx proc nearπ ; get next byte of data from a buffer or somethingπ ; (remember to set the segment registers correctly!)π ; and write it to the THR (offset 0)π ; if no more data is to be sent, disable the THRE interruptπ ; If the FIFO is used, you can write data as long as bit 5π ; of the LSR is 0ππ ; end of data to be sent?π ; no, jump to end_int_txπ mov dx,UART_BASEADDR+1π in al,dxπ and al,00001101bπ out dx,alπend_int_tx:π retπint_tx endpππint_rx proc nearπ mov dx,UART_BASEADDRπ in al,dxπ ; do with the character what you like (best write it to aπ ; FIFO buffer)π ; the following lines speed up FIFO mode operationπ mov dx,UART_BASEADDR+5π in al,dxπ test al,1π jnz int_rxπ retπint_rx endpππint_status proc nearπ mov dx,UART_BASEADDR+5π in al,dxπ ; do what you like. It's just important to read the LSRπ retπint_status endpππ How is data sent now? Write it to a FIFO buffer that is read by theπinterrupt routine. Then set bit 1 of the IER and check if this has alreadyπstarted transmission. If not, you'll have to start it by yourself... THISπIS DUE TO THOSE NUTTY GUYS AT BIG BLUE WHO DECIDED TO USE EDGE TRIGGEREDπINTERRUPTS INSTEAD OF PROVIDING ONE SINGLE FLIP FLOP FOR THE 8253/8254!π This procedure can be a C function, too. It is not time-critical at all.ππ ; copy data to bufferπ mov dx,UART_BASEADDR+1 ; IERπ in al,dxπ or al,2 ; set bit 1π out dx,alπ mov dx,UART_BASEADDR+5 ; LSRπ in al,dxπ test al,40h ; is there a transmission running?π jz dont_crank ; yes, so don't mess it upπ call int_tx ; no, crank it upπdont_crank:ππ Well, that's it! Your main program has to take care about the buffers,πnothing else!ππ One more thing: always remember that at 115,200 baud there is service toπbe done at least every 8 microseconds! On an XT with 4.77 MHz this isπabout 5 assembler commands!! So forget about servicing the serial port atπthis rate in an interrupt-driven manner on such computers. An AT with 12πMHz probably will manage it if you use 'macro commands' such as pusha and/orπa 16550A in FIFO mode. An AT can perform about 20 instructions between twoπcharacters, a 386 with 25 MHz will do about 55, and a 486 with 33 MHz willπmanage about 150. Using a 16550A is strongly recommended at high rates.π The interrupt service routines can be accelerated by not pushing thatπmuch registers, and pusha and popa are fast instructions.ππ Another last thing: due to the poor construction of the PC interruptπsystem, one interrupt line can only be driven by one device. This means ifπyou want to use COM3 and your mouse is connected to COM1, you can't useπinterrupt features without disabling the mouse (write 0x0 to the mouse'sπMCR).π